mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2025-02-25 18:55:21 -06:00
[PATCH] Upstream patch - 16022023
This commit is contained in:
@@ -261,7 +261,7 @@ class AccountMove(models.Model):
|
||||
|
||||
# ==== Reverse feature fields ====
|
||||
reversed_entry_id = fields.Many2one('account.move', string="Reversal of", readonly=True, copy=False,
|
||||
check_company=True)
|
||||
check_company=True, index=True)
|
||||
reversal_move_id = fields.One2many('account.move', 'reversed_entry_id')
|
||||
|
||||
# =========================================================
|
||||
|
||||
@@ -135,7 +135,9 @@ def update_taxes_from_templates(cr, chart_template_xmlid):
|
||||
if not fp:
|
||||
continue
|
||||
for position_tax in position_template.tax_ids:
|
||||
if position_tax.tax_src_id in new_taxes_template or position_tax.tax_dest_id in new_taxes_template:
|
||||
position_tax_template_exist = fp.tax_ids.filtered_domain([('tax_src_id', '=', tax_template_ref[position_tax.tax_src_id.id]),
|
||||
('tax_dest_id', '=', position_tax.tax_dest_id and tax_template_ref[position_tax.tax_dest_id.id] or False)])
|
||||
if not position_tax_template_exist and (position_tax.tax_src_id in new_taxes_template or position_tax.tax_dest_id in new_taxes_template):
|
||||
tax_template_vals.append((position_tax, {
|
||||
'tax_src_id': tax_template_ref[position_tax.tax_src_id.id],
|
||||
'tax_dest_id': position_tax.tax_dest_id and tax_template_ref[position_tax.tax_dest_id.id] or False,
|
||||
|
||||
@@ -469,6 +469,9 @@ class ResCompany(models.Model):
|
||||
"""Checks that all posted moves have still the same data as when they were posted
|
||||
and raises an error with the result.
|
||||
"""
|
||||
if not self.env.user.has_group('account.group_account_user'):
|
||||
raise UserError(_('Please contact your accountant to print the Hash integrity result.'))
|
||||
|
||||
def build_move_info(move):
|
||||
return(move.name, move.inalterable_hash, fields.Date.to_string(move.date))
|
||||
|
||||
@@ -496,8 +499,11 @@ class ResCompany(models.Model):
|
||||
results_by_journal['results'].append(rslt)
|
||||
continue
|
||||
|
||||
all_moves_count = self.env['account.move'].search_count([('state', '=', 'posted'), ('journal_id', '=', journal.id)])
|
||||
moves = self.env['account.move'].search([('state', '=', 'posted'), ('journal_id', '=', journal.id),
|
||||
# We need the `sudo()` to ensure that all the moves are searched, no matter the user's access rights.
|
||||
# This is required in order to generate consistent hashs.
|
||||
# It is not an issue, since the data is only used to compute a hash and not to return the actual values.
|
||||
all_moves_count = self.env['account.move'].sudo().search_count([('state', '=', 'posted'), ('journal_id', '=', journal.id)])
|
||||
moves = self.env['account.move'].sudo().search([('state', '=', 'posted'), ('journal_id', '=', journal.id),
|
||||
('secure_sequence_number', '!=', 0)], order="secure_sequence_number ASC")
|
||||
if not moves:
|
||||
rslt.update({
|
||||
|
||||
@@ -230,6 +230,7 @@ class HrEmployeePrivate(models.Model):
|
||||
vals.update(self._sync_user(user, vals.get('image_1920') == self._default_image()))
|
||||
vals['name'] = vals.get('name', user.name)
|
||||
employee = super(HrEmployeePrivate, self).create(vals)
|
||||
employee.message_subscribe(employee.address_home_id.ids)
|
||||
url = '/web#%s' % url_encode({
|
||||
'action': 'hr.plan_wizard_action',
|
||||
'active_id': employee.id,
|
||||
@@ -248,6 +249,8 @@ class HrEmployeePrivate(models.Model):
|
||||
account_id = vals.get('bank_account_id') or self.bank_account_id.id
|
||||
if account_id:
|
||||
self.env['res.partner.bank'].browse(account_id).partner_id = vals['address_home_id']
|
||||
self.message_unsubscribe(self.address_home_id.ids)
|
||||
self.message_subscribe([vals['address_home_id']])
|
||||
if vals.get('user_id'):
|
||||
# Update the profile pictures with user, except if provided
|
||||
vals.update(self._sync_user(self.env['res.users'].browse(vals['user_id']), bool(vals.get('image_1920'))))
|
||||
|
||||
@@ -7,6 +7,7 @@ class AccountMove(models.Model):
|
||||
|
||||
l10n_de_template_data = fields.Binary(compute='_compute_l10n_de_template_data')
|
||||
l10n_de_document_title = fields.Char(compute='_compute_l10n_de_document_title')
|
||||
l10n_de_addresses = fields.Binary(compute='_compute_l10n_de_addresses')
|
||||
|
||||
def _compute_l10n_de_template_data(self):
|
||||
for record in self:
|
||||
@@ -38,3 +39,14 @@ class AccountMove(models.Model):
|
||||
record.l10n_de_document_title = _('Vendor Credit Note')
|
||||
elif record.move_type == 'in_invoice':
|
||||
record.l10n_de_document_title = _('Vendor Bill')
|
||||
|
||||
def _compute_l10n_de_addresses(self):
|
||||
for record in self:
|
||||
record.l10n_de_addresses = data = []
|
||||
if record.partner_shipping_id == record.partner_id:
|
||||
data.append((_("Invoicing and Shipping Address:"), record.partner_shipping_id))
|
||||
elif record.move_type in ("in_invoice", "in_refund"):
|
||||
data.append((_("Invoicing and Shipping Address:"), record.partner_id))
|
||||
else:
|
||||
data.append((_("Shipping Address:"), record.partner_shipping_id))
|
||||
data.append((_("Invoicing Address:"), record.partner_id))
|
||||
|
||||
@@ -3841,6 +3841,16 @@
|
||||
<field name="tax_src_id" ref="l10n_es.account_tax_template_p_iva4_bc"/>
|
||||
<field name="tax_dest_id" ref="l10n_es.account_tax_template_p_irpf2"/>
|
||||
</record>
|
||||
<record id="fptt_reagyp_a_4b_3" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fp_reagyp_a"/>
|
||||
<field name="tax_src_id" ref="account_tax_template_p_iva0_s_bc"/>
|
||||
<field name="tax_dest_id" ref="account_tax_template_p_iva12_agr"/>
|
||||
</record>
|
||||
<record id="fptt_reagyp_a_4b_4" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fp_reagyp_a"/>
|
||||
<field name="tax_src_id" ref="account_tax_template_p_iva0_s_bc"/>
|
||||
<field name="tax_dest_id" ref="account_tax_template_p_irpf2"/>
|
||||
</record>
|
||||
<record id="fptt_reagyp_gp_4b_1" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fp_reagyp_gp"/>
|
||||
<field name="tax_src_id" ref="l10n_es.account_tax_template_p_iva4_bc"/>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- Don't put move_id here to avoid that the framework send falsy move_id -->
|
||||
<field name="id" invisible="1"/>
|
||||
<field name="product_id" required="1"/>
|
||||
<field name="lot_id"
|
||||
<field name="lot_id" groups="stock.group_production_lot"
|
||||
attrs="{'invisible': [('tracking', 'not in', ('serial', 'lot'))], 'required': [('tracking', 'in', ('serial', 'lot'))]}"
|
||||
context="{'default_product_id': product_id, 'default_company_id': company_id}"/>
|
||||
<field name="product_uom_qty" readonly="1" force_save="1"/>
|
||||
@@ -67,7 +67,7 @@
|
||||
<field name="location_dest_id" invisible="1"/>
|
||||
<field name="product_id" readonly="1" force_save="1"/>
|
||||
<field name="qty_done"/>
|
||||
<field name="lot_id" attrs="{'column_invisible':[('parent.has_tracking', 'not in', ('serial', 'lot'))], 'required': [('tracking', 'in', ('serial', 'lot'))]}" context="{'default_product_id': product_id, 'default_company_id': company_id}"/>
|
||||
<field name="lot_id" attrs="{'column_invisible':[('parent.has_tracking', 'not in', ('serial', 'lot'))], 'required': [('tracking', 'in', ('serial', 'lot'))]}" context="{'default_product_id': product_id, 'default_company_id': company_id}" groups="stock.group_production_lot"/>
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
|
||||
@@ -84,8 +84,8 @@ class AuthorizeAPI():
|
||||
'paymentProfiles': {
|
||||
'customerType': 'business' if partner.is_company else 'individual',
|
||||
'billTo': {
|
||||
'firstName': '' if partner.is_company else _partner_split_name(partner.name)[0],
|
||||
'lastName': _partner_split_name(partner.name)[1],
|
||||
'firstName': '' if partner.is_company else _partner_split_name(partner.name)[0][:50],
|
||||
'lastName': partner.name[:50] if partner.is_company else _partner_split_name(partner.name)[1][:50],
|
||||
'address': (partner.street or '' + (partner.street2 if partner.street2 else '')) or None,
|
||||
'city': partner.city,
|
||||
'state': partner.state_id.name or None,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_phonenumbers
|
||||
from . import test_phonenumbers_patch
|
||||
|
||||
23
addons/phone_validation/tests/test_phonenumbers.py
Normal file
23
addons/phone_validation/tests/test_phonenumbers.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra.addons.phone_validation.tools import phone_validation
|
||||
from flectra.exceptions import UserError
|
||||
from flectra.tests import tagged
|
||||
from flectra.tests.common import BaseCase
|
||||
|
||||
|
||||
@tagged('phone_validation')
|
||||
class TestPhonenumbers(BaseCase):
|
||||
|
||||
def test_country_code_falsy(self):
|
||||
self.assertEqual(
|
||||
phone_validation.phone_format('0456998877', 'BE', '32', force_format='E164'),
|
||||
'+32456998877'
|
||||
)
|
||||
# no country code -> UserError, no internal traceback
|
||||
with self.assertRaises(UserError):
|
||||
self.assertEqual(
|
||||
phone_validation.phone_format('0456998877', None, '32', force_format='E164'),
|
||||
'+32456998877'
|
||||
)
|
||||
@@ -15,7 +15,7 @@ try:
|
||||
|
||||
def phone_parse(number, country_code):
|
||||
try:
|
||||
phone_nbr = phonenumbers.parse(number, region=country_code, keep_raw_input=True)
|
||||
phone_nbr = phonenumbers.parse(number, region=country_code or None, keep_raw_input=True)
|
||||
except phonenumbers.phonenumberutil.NumberParseException as e:
|
||||
raise UserError(_('Unable to parse %(phone)s: %(error)s', phone=number, error=str(e)))
|
||||
|
||||
|
||||
@@ -106,6 +106,13 @@ flectra.define('point_of_sale.ReceiptScreen', function (require) {
|
||||
const orderName = order.get_name();
|
||||
const orderClient = { email: this.orderUiState.inputEmail, name: client ? client.name : this.orderUiState.inputEmail };
|
||||
const order_server_id = this.env.pos.validated_orders_name_server_id_map[orderName];
|
||||
if (!order_server_id) {
|
||||
this.showPopup('ErrorPopup', {
|
||||
title: 'Unsynced order',
|
||||
body: 'This order is not yet synced to server. Make sure it is synced then try again.',
|
||||
});
|
||||
return Promise.reject();
|
||||
}
|
||||
await this.rpc({
|
||||
model: 'pos.order',
|
||||
method: 'action_receipt_to_customer',
|
||||
|
||||
@@ -637,7 +637,7 @@ class Task(models.Model):
|
||||
allow_subtasks = fields.Boolean(string="Allow Sub-tasks", related="project_id.allow_subtasks", readonly=True)
|
||||
subtask_count = fields.Integer("Sub-task count", compute='_compute_subtask_count')
|
||||
email_from = fields.Char(string='Email From', help="These people will receive email.", index=True,
|
||||
compute='_compute_email_from', store="True", readonly=False)
|
||||
compute='_compute_email_from', store="True", readonly=False, copy=False)
|
||||
allowed_user_ids = fields.Many2many('res.users', string="Visible to", groups='project.group_project_manager', compute='_compute_allowed_user_ids', store=True, readonly=False, copy=False)
|
||||
project_privacy_visibility = fields.Selection(related='project_id.privacy_visibility', string="Project Visibility")
|
||||
# Computed field about working time elapsed between record creation and assignation/closing.
|
||||
|
||||
@@ -2162,6 +2162,20 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
|
||||
invoice = so._create_invoices()
|
||||
invoice.action_post()
|
||||
|
||||
# Receive one @100
|
||||
in_moves = self.env['stock.move'].create({
|
||||
'name': 'IN move @100',
|
||||
'product_id': self.component_a.id,
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||||
'product_uom': self.component_a.uom_id.id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': 100,
|
||||
})
|
||||
in_moves._action_confirm()
|
||||
in_moves.quantity_done = 1
|
||||
in_moves._action_done()
|
||||
|
||||
# Return the second picking (i.e. one component @20)
|
||||
ctx = {'active_id': pickings[1].id, 'active_model': 'stock.picking'}
|
||||
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
|
||||
@@ -2259,6 +2273,20 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
|
||||
invoice = so._create_invoices()
|
||||
invoice.action_post()
|
||||
|
||||
# Receive one @100
|
||||
in_moves = self.env['stock.move'].create({
|
||||
'name': 'IN move @100',
|
||||
'product_id': self.component_a.id,
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||||
'product_uom': self.component_a.uom_id.id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': 100,
|
||||
})
|
||||
in_moves._action_confirm()
|
||||
in_moves.quantity_done = 1
|
||||
in_moves._action_done()
|
||||
|
||||
# Return the second picking (i.e. one component @20)
|
||||
ctx = {'active_id': pickings[1].id, 'active_model': 'stock.picking'}
|
||||
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
|
||||
|
||||
@@ -201,7 +201,7 @@ class Website(Home):
|
||||
sitemaps.unlink()
|
||||
|
||||
pages = 0
|
||||
locs = request.website.with_user(request.website.user_id)._enumerate_pages()
|
||||
locs = request.website.with_context(_filter_duplicate_pages=True).with_user(request.website.user_id)._enumerate_pages()
|
||||
while True:
|
||||
values = {
|
||||
'locs': islice(locs, 0, LOC_PER_SITEMAP),
|
||||
@@ -264,7 +264,7 @@ class Website(Home):
|
||||
current_website = request.website
|
||||
|
||||
matching_pages = []
|
||||
for page in current_website.search_pages(needle, limit=int(limit)):
|
||||
for page in current_website.with_context(_filter_duplicate_pages=True).search_pages(needle, limit=int(limit)):
|
||||
matching_pages.append({
|
||||
'value': page['loc'],
|
||||
'label': 'name' in page and '%s (%s)' % (page['loc'], page['name']) or page['loc'],
|
||||
@@ -272,7 +272,7 @@ class Website(Home):
|
||||
matching_urls = set(map(lambda match: match['value'], matching_pages))
|
||||
|
||||
matching_last_modified = []
|
||||
last_modified_pages = current_website._get_website_pages(order='write_date desc', limit=5)
|
||||
last_modified_pages = current_website.with_context(_filter_duplicate_pages=True)._get_website_pages(order='write_date desc', limit=5)
|
||||
for url, name in last_modified_pages.mapped(lambda p: (p.url, p.name)):
|
||||
if needle.lower() in name.lower() or needle.lower() in url.lower() and url not in matching_urls:
|
||||
matching_last_modified.append({
|
||||
|
||||
@@ -892,6 +892,9 @@ class Website(models.Model):
|
||||
domain = []
|
||||
domain += self.get_current_website().website_domain()
|
||||
pages = self.env['website.page'].sudo().search(domain, order=order, limit=limit)
|
||||
# TODO In 16.0 remove condition on _filter_duplicate_pages.
|
||||
if self.env.context.get('_filter_duplicate_pages'):
|
||||
pages = pages.filtered(pages._is_most_specific_page)
|
||||
return pages
|
||||
|
||||
def search_pages(self, needle=None, limit=None):
|
||||
|
||||
Reference in New Issue
Block a user