[PATCH] Upstream patch - 07082023

This commit is contained in:
Parthiv Patel committed 2023-08-07 08:35:19 +00:00
1 parent 2e9745f156
commit 59ad00617d
18 files changed
+165 -68

No files matched your search

+16 -2
View File
@@ -3102,8 +3102,22 @@ class AccountMove(models.Model):
('date', '<=', fields.Date.context_today(self)),
('auto_post', '=', True),
])
for ids in self._cr.split_for_in_conditions(records.ids, size=1000):
self.browse(ids)._post()
for ids in self._cr.split_for_in_conditions(records.ids, size=100):
moves = self.browse(ids)
try: # try posting in batch
moves._post()
except UserError: # if at least one move cannot be posted, handle moves one by one
for move in moves:
try:
move._post()
except UserError as e:
move.to_check = True
msg = _('The move could not be posted for the following reason: %(error_message)s', error_message=e)
move.message_post(body=msg,
message_type='comment',
author_id=self.env.ref('base.partner_root').id)
if not self.env.registry.in_test_mode():
self._cr.commit()
+15 -5
View File
@@ -44,7 +44,7 @@ def update_taxes_from_templates(cr, chart_template_xmlid):
This method is mainly used as a local upgrade script.
Returns a list of tuple (template_id, tax_id) of newly created records.
"""
def _create_taxes_from_template(company, template2tax_mapping):
def _create_taxes_from_template(company, template2tax_mapping, template2tax_to_update=None):
""" Create a new taxes from templates. If an old tax already used the same xmlid, we
remove the xmlid from it but don't modify anything else.
:param company: the company of the tax to instantiate
@@ -72,7 +72,9 @@ def update_taxes_from_templates(cr, chart_template_xmlid):
_remove_xml_id(xml_id)
_avoid_name_conflict(company, template)
templates_to_create += template
new_template2tax_company = templates_to_create._generate_tax(company, accounts_exist=True)['tax_template_to_tax']
new_template2tax_company = templates_to_create._generate_tax(
company, accounts_exist=True, existing_template_to_tax=template2tax_to_update
)['tax_template_to_tax']
return new_template2tax_company.items()
def _update_taxes_from_template(template2tax_mapping):
@@ -240,7 +242,7 @@ def update_taxes_from_templates(cr, chart_template_xmlid):
new_tax_template_by_company[company.id] += template
else:
templates_to_tax_update.append((template, tax))
new_template2tax += _create_taxes_from_template(company, templates_to_tax_create)
new_template2tax += _create_taxes_from_template(company, templates_to_tax_create, templates_to_tax_update)
_update_taxes_from_template(templates_to_tax_update)
_update_fiscal_positions_from_templates(chart_template, new_tax_template_by_company, templates)
if outdated_taxes:
@@ -1177,10 +1179,12 @@ class AccountTaxTemplate(models.Model):
})
return vals
def _generate_tax(self, company, accounts_exist=False):
def _generate_tax(self, company, accounts_exist=False, existing_template_to_tax=None):
""" This method generate taxes from templates.
:param company: the company for which the taxes should be created from templates in self
:account_exist: whether accounts have already been created
:existing_template_to_tax: mapping of already existing templates to taxes [(template, tax), ...]
:returns: {
'tax_template_to_tax': mapping between tax template and the newly generated taxes corresponding,
'account_dict': dictionary containing a to-do list with all the accounts to assign on new taxes
@@ -1190,7 +1194,9 @@ class AccountTaxTemplate(models.Model):
# repartition lines on taxes
ChartTemplate = self.env['account.chart.template'].with_context(default_company_id=company.id)
todo_dict = {'account.tax': {}, 'account.tax.repartition.line': {}}
tax_template_to_tax = {}
if not existing_template_to_tax:
existing_template_to_tax = []
tax_template_to_tax = {template.id: tax.id for (template, tax) in existing_template_to_tax}
templates_todo = list(self)
while templates_todo:
@@ -1219,6 +1225,10 @@ class AccountTaxTemplate(models.Model):
'cash_basis_transition_account_id': template.cash_basis_transition_account_id.id,
}
for existing_template, existing_tax in existing_template_to_tax:
if template in existing_template.children_tax_ids and tax not in existing_tax.children_tax_ids:
existing_tax.write({'children_tax_ids': [(4, tax.id, False)]})
# We also have to delay the assignation of accounts to repartition lines
# The below code assigns the account_id to the repartition lines according
# to the corresponding repartition line in the template, based on the order.
@@ -3591,3 +3591,37 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
self.assertRecordValues(caba_move, [{
'date': fields.Date.from_string('2023-01-30'),
}])
@freeze_time('2023-01-01')
def test_post_valid_invoices_when_auto_post(self):
valid_invoice = self.init_invoice(move_type='out_invoice', products=self.product_a, invoice_date='2023-01-01')
# missing partner
invalid_invoice_1 = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2023-01-01',
'date': '2023-01-01',
'invoice_line_ids': [(0, 0, {
'name': 'test line',
'price_unit': 10,
'quantity': 1,
'account_id': self.company_data['default_account_revenue'].id,
})],
})
# missing invoice lines
invalid_invoice_2 = self.init_invoice(move_type='out_invoice', invoice_date='2023-01-01')
(valid_invoice + invalid_invoice_1 + invalid_invoice_2).auto_post = True
self.registry.enter_test_mode(self.env.cr)
self.env['account.move']._autopost_draft_entries()
self.registry.leave_test_mode()
self.assertEqual(valid_invoice.state, 'posted')
self.assertEqual(invalid_invoice_1.state, 'draft')
self.assertEqual(invalid_invoice_1.message_ids[0].body,
"<p>The move could not be posted for the following reason: "
"The field 'Customer' is required, please complete it to validate the Customer Invoice.</p>")
self.assertEqual(invalid_invoice_2.state, 'draft')
self.assertEqual(invalid_invoice_2.message_ids[0].body,
"<p>The move could not be posted for the following reason: You need to add a line before posting.</p>")
@@ -331,6 +331,7 @@ class TestChartTemplate(SavepointCase):
""" Ensures children_tax_ids are correctly generated when updating taxes with
amount_type='group'.
"""
# Both parent and its two children should be created.
group_tax_name = 'Group Tax name 1 TEST'
self._create_group_tax_template('account.test_group_tax_test_template', group_tax_name,
chart_template_id=self.chart_template.id)
@@ -349,6 +350,28 @@ class TestChartTemplate(SavepointCase):
self.assertEqual(parent_tax.children_tax_ids.ids, children_taxes.ids,
"The parent and its children taxes should be linked together.")
# Parent exists - only the two children should be created.
children_taxes.unlink()
update_taxes_from_templates(self.env.cr, self.chart_template_xmlid)
children_taxes = self.env['account.tax'].search([
('company_id', '=', self.company.id),
('name', 'like', f'{group_tax_name}_%'),
])
self.assertEqual(len(children_taxes), 2, "Two children should be re-created.")
self.assertEqual(parent_tax.children_tax_ids.ids, children_taxes.ids,
"The parent and its children taxes should be linked together.")
# Children exist - only the parent should be created.
parent_tax.unlink()
update_taxes_from_templates(self.env.cr, self.chart_template_xmlid)
parent_tax = self.env['account.tax'].search([
('company_id', '=', self.company.id),
('name', '=', group_tax_name),
])
self.assertEqual(len(parent_tax), 1, "The parent tax should have been re-created.")
self.assertEqual(parent_tax.children_tax_ids.ids, children_taxes.ids,
"The parent and its children taxes should be linked together.")
def test_update_taxes_children_tax_ids_inactive(self):
""" Ensure tax templates are correctly generated when updating taxes with children taxes,
even if templates are inactive.
+2 -2
View File
@@ -174,8 +174,8 @@
</tr>
</t>
<t t-if="len(payments_vals) > 0">
<tr class="border-black">
<td><strong>Amount Due</strong></td>
<tr class="border-black font-weight-bold">
<td>Amount Due</td>
<td class="text-right">
<span t-field="o.amount_residual"/>
</td>
+1 -1
View File
@@ -204,7 +204,7 @@
<field name="name">hr.expense.kanban</field>
<field name="model">hr.expense</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile hr_expense" sample="1" js_class="hr_expense_kanban">
<kanban class="o_kanban_mobile hr_expense" sample="1" js_class="hr_expense_kanban" quick_create="false">
<field name="name"/>
<field name="employee_id"/>
<field name="total_amount"/>
+1 -1
View File
@@ -46,7 +46,7 @@ class AccountAnalyticLine(models.Model):
task_id = fields.Many2one(
'project.task', 'Task', compute='_compute_task_id', store=True, readonly=False, index=True,
domain="[('company_id', '=', company_id), ('project_id.allow_timesheets', '=', True), ('project_id', '=?', project_id)]")
domain="[('project_id.allow_timesheets', '=', True), ('project_id', '=?', project_id)]")
project_id = fields.Many2one(
'project.project', 'Project', compute='_compute_project_id', store=True, readonly=False,
domain=_domain_project_id)
-1
View File
@@ -111,7 +111,6 @@ Master Data:
'demo/mono_demo.xml',
'demo/respinsc_demo.xml',
'demo/res_partner_demo.xml',
'demo/account_tax_demo.xml',
'demo/product_product_demo.xml',
'demo/account_customer_invoice_demo.xml',
'demo/account_customer_refund_demo.xml',
@@ -305,21 +305,6 @@
<value eval="[ref('demo_invoice_16')]"/>
</function>
<function model="account.move.line" name="write" context="{'check_move_validity': False, 'active_test': False}">
<value model="account.move.line" search="[('move_id', '=', ref('demo_invoice_19')), ('product_id', '=', ref('product.product_product_2'))]"/>
<value model="account.tax" eval="{'tax_ids': [(4, obj().search([('company_id', '=', ref('company_ri')), ('type_tax_use', '=', 'sale'), ('tax_group_id.l10n_ar_tribute_afip_code', '=', '06')], limit=1).id)]}"/>
</function>
<function model="account.move.line" name="write" context="{'check_move_validity': False, 'active_test': False}">
<value model="account.move.line" search="[('move_id', '=', ref('demo_invoice_19')), ('product_id', '=', ref('product_product_telefonia'))]"/>
<value model="account.tax" eval="{'tax_ids': [(4, obj().search([('company_id', '=', ref('company_ri')), ('type_tax_use', '=', 'sale'), ('tax_group_id.l10n_ar_tribute_afip_code', '=', '07')], limit=1).id)]}"/>
</function>
<function model="account.move.line" name="write" context="{'check_move_validity': False, 'active_test': False}">
<value model="account.move.line" search="[('move_id', '=', ref('demo_invoice_19')), ('product_id', '=', ref('product.product_product_25'))]"/>
<value model="account.tax" eval="{'tax_ids': [(4, obj().search([('company_id', '=', ref('company_ri')), ('type_tax_use', '=', 'sale'), ('tax_group_id.l10n_ar_tribute_afip_code', '=', '99')], limit=1).id)]}"/>
</function>
<function model="account.move" name="_recompute_dynamic_lines" context="{'check_move_validity': False}">
<value eval="[ref('demo_invoice_1'), ref('demo_invoice_2'), ref('demo_invoice_3'), ref('demo_invoice_4'), ref('demo_invoice_5'), ref('demo_invoice_6'), ref('demo_invoice_7'), ref('demo_invoice_8'), ref('demo_invoice_10'), ref('demo_invoice_11'), ref('demo_invoice_12'), ref('demo_invoice_13'), ref('demo_invoice_14'), ref('demo_invoice_15'), ref('demo_invoice_16'), ref('demo_invoice_17'), ref('demo_invoice_18'), ref('demo_invoice_19')]"/>
<value eval="True"/>
@@ -288,21 +288,6 @@
<value model="account.move.line" eval="obj().search([('move_id', 'in', [ref('demo_sup_invoice_1'), ref('demo_sup_invoice_2'), ref('demo_sup_invoice_3'), ref('demo_sup_invoice_4'), ref('demo_sup_invoice_5'), ref('demo_sup_invoice_6'), ref('demo_sup_invoice_7'), ref('demo_sup_invoice_8')])]).ids"/>
</function>
<function model="account.move.line" name="write" context="{'check_move_validity': False, 'active_test': False}">
<value model="account.move.line" search="[('move_id', '=', ref('demo_sup_invoice_8')), ('product_id', '=', ref('product.product_product_27'))]"/>
<value model="account.tax" eval="{'tax_ids': [(4, obj().search([('company_id', '=', ref('company_ri')), ('type_tax_use', '=', 'purchase'), ('tax_group_id.l10n_ar_tribute_afip_code', '=', '06')], limit=1).id)]}"/>
</function>
<function model="account.move.line" name="write" context="{'check_move_validity': False, 'active_test': False}">
<value model="account.move.line" search="[('move_id', '=', ref('demo_sup_invoice_8')), ('product_id', '=', ref('product_product_telefonia'))]"/>
<value model="account.tax" eval="{'tax_ids': [(4, obj().search([('company_id', '=', ref('company_ri')), ('type_tax_use', '=', 'purchase'), ('tax_group_id.l10n_ar_tribute_afip_code', '=', '07')], limit=1).id)]}"/>
</function>
<function model="account.move.line" name="write" context="{'check_move_validity': False, 'active_test': False}">
<value model="account.move.line" search="[('move_id', '=', ref('demo_sup_invoice_8')), ('product_id', '=', ref('product.product_product_25'))]"/>
<value model="account.tax" eval="{'tax_ids': [(4, obj().search([('company_id', '=', ref('company_ri')), ('type_tax_use', '=', 'purchase'), ('tax_group_id.l10n_ar_tribute_afip_code', '=', '99')], limit=1).id)]}"/>
</function>
<function model="account.move" name="_recompute_dynamic_lines" context="{'check_move_validity': False}">
<value eval="[ref('demo_sup_invoice_1'), ref('demo_sup_invoice_2'), ref('demo_sup_invoice_3'), ref('demo_sup_invoice_4'), ref('demo_sup_invoice_5'), ref('demo_sup_invoice_6'), ref('demo_sup_invoice_7'), ref('demo_sup_invoice_8'), ref('demo_despacho_1')]"/>
<value eval="True"/>
@@ -106,6 +106,7 @@ class Company(models.Model):
'code': new_code,
'user_type_id': sales_tax_accounts[0].user_type_id.id,
'company_id': self.id,
'tag_ids': [(4, tag.id, 0) for tag in sales_tax_accounts[0].tag_ids],
})
self.env['ir.model.data'].create({
'name': f'oss_tax_account_company_{self.id}',
+37 -17
View File
@@ -302,7 +302,8 @@ class AccountFrFec(models.TransientModel):
rows_to_write.append(listrow)
# LINES
sql_query = '''
query_limit = int(self.env['ir.config_parameter'].sudo().get_param('l10n_fr_fec.batch_size', 500000)) # To prevent memory errors when fetching the results
sql_query = f'''
SELECT
REGEXP_REPLACE(replace(aj.code, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalCode,
REGEXP_REPLACE(replace(COALESCE(aj__name.value, aj.name), '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalLib,
@@ -360,35 +361,54 @@ class AccountFrFec(models.TransientModel):
am.date >= %s
AND am.date <= %s
AND am.company_id = %s
'''
# For official report: only use posted entries
if self.export_type == "official":
sql_query += '''
AND am.state = 'posted'
'''
sql_query += '''
{"AND am.state = 'posted'" if self.export_type == 'official' else ""}
ORDER BY
am.date,
am.name,
aml.id
LIMIT %s
OFFSET %s
'''
lang = self.env.user.lang or get_lang(self.env).code
self._cr.execute(
sql_query, (lang, self.date_from, self.date_to, company.id))
for row in self._cr.fetchall():
rows_to_write.append(list(row))
with io.BytesIO() as fecfile:
csv_writer = pycompat.csv_writer(fecfile, delimiter='|', lineterminator='')
# Write header and initial balances
for initial_row in rows_to_write:
initial_row = list(initial_row)
# We don't skip \n at then end of the file if there are only initial balances, for simplicity. An empty period export shouldn't happen IRL.
initial_row[-1] += u'\r\n'
csv_writer.writerow(initial_row)
# Write current period's data
query_offset = 0
has_more_results = True
while has_more_results:
self._cr.execute(
sql_query,
(lang, self.date_from, self.date_to, company.id, query_limit + 1, query_offset)
)
query_offset += query_limit
has_more_results = self._cr.rowcount > query_limit # we load one more result than the limit to check if there is more
query_results = self._cr.fetchall()
for i, row in enumerate(query_results[:query_limit]):
if i < len(query_results) - 1:
# The file is not allowed to end with an empty line, so we can't use lineterminator on the writer
row = list(row)
row[-1] += u'\r\n'
csv_writer.writerow(row)
base64_result = base64.encodebytes(fecfile.getvalue())
fecvalue = self._csv_write_rows(rows_to_write)
end_date = fields.Date.to_string(self.date_to).replace('-', '')
suffix = ''
if self.export_type == "nonofficial":
suffix = '-NONOFFICIAL'
self.write({
'fec_data': base64.encodebytes(fecvalue),
'fec_data': base64_result,
# Filename = <siren>FECYYYYMMDD where YYYMMDD is the closing date
'filename': '%sFEC%s%s.csv' % (company_legal_data, end_date, suffix),
})
@@ -404,7 +424,7 @@ class AccountFrFec(models.TransientModel):
'target': 'self',
}
def _csv_write_rows(self, rows, lineterminator=u'\r\n'):
def _csv_write_rows(self, rows, lineterminator=u'\r\n'): #DEPRECATED; will disappear in master
"""
Write FEC rows into a file
It seems that Bercy's bureaucracy is not too happy about the
@@ -11,7 +11,7 @@ class PosPaymentMethod(models.Model):
def _get_adyen_endpoints(self):
return {
**super(PosPaymentMethod, self).get_adyen_endpoints(),
**super(PosPaymentMethod, self)._get_adyen_endpoints(),
'adjust': 'https://pal-%s.adyen.com/pal/servlet/Payment/v52/adjustAuthorisation',
'capture': 'https://pal-%s.adyen.com/pal/servlet/Payment/v52/capture',
}
@@ -801,12 +801,12 @@ var FieldDateRange = InputField.extend({
_getDateRangeFromInputField() {
let startDate, endDate;
if (this.relatedEndDate) {
startDate = this.value;
endDate = this.recordData[this.relatedEndDate];
startDate = this._getValue();
endDate = field_utils.parse[this.formatType](this.recordData[this.relatedEndDate]);
}
if (this.relatedStartDate) {
startDate = this.recordData[this.relatedStartDate];
endDate = this.value;
startDate = field_utils.parse[this.formatType](this.recordData[this.relatedStartDate]);
endDate = this._getValue();
}
return [startDate, endDate];
},
@@ -3472,7 +3472,7 @@ QUnit.module('basic_fields', {
QUnit.module('FieldDateRange');
QUnit.test('Datetime field [REQUIRE FOCUS]', async function (assert) {
assert.expect(20);
assert.expect(22);
this.data.partner.fields.datetime_end = {string: 'Datetime End', type: 'datetime'};
this.data.partner.records[0].datetime_end = '2017-03-13 00:00:00';
@@ -3529,6 +3529,16 @@ QUnit.module('basic_fields', {
assert.strictEqual($('.daterangepicker:first').css('display'), 'none',
"date range picker should be closed");
// Discard form, fields shouldn't be altered
await testUtils.form.clickDiscard(form);
assert.strictEqual(form.$('.o_field_date_range:first').text(), '02/08/2017 15:30:00',
"the start date should be the same as before editing");
assert.strictEqual(form.$('.o_field_date_range:last').text(), '03/13/2017 05:30:00',
"the end date should be the same as before editing");
// Edit
await testUtils.form.clickEdit(form);
// Try to check with end date
await testUtils.dom.click(form.$('.o_field_date_range:last'));
assert.strictEqual($('.daterangepicker:last').css('display'), 'block',
@@ -33,7 +33,9 @@ $o-we-border-color: $o-we-bg-light !default;
$o-we-ace-color: #2F3129 !default;
$o-we-handles-offset-to-hide: 10000px !default;
// Needed to be changed to be high enough to not overflow when a user
// has a page with a lot of content (10000px was proven to be too small)
$o-we-handles-offset-to-hide: 100000px !default;
$o-we-handles-btn-size: 20px !default;
$o-we-handles-accent-color: $o-we-accent !default;
$o-we-handles-accent-color-preview: $o-enterprise-color !default;
@@ -16,12 +16,16 @@ const TableOfContent = publicWidget.Widget.extend({
this.$scrollingElement = this.$target.closest(".s_table_of_content").closestScrollable();
this.previousPosition = -1;
this._updateTableOfContentNavbarPosition();
extraMenuUpdateCallbacks.push(this._updateTableOfContentNavbarPosition.bind(this));
this.boundUpdateNavbar = this._updateTableOfContentNavbarPosition.bind(this);
extraMenuUpdateCallbacks.push(this.boundUpdateNavbar);
},
/**
* @override
*/
destroy() {
const indexOfCallback = extraMenuUpdateCallbacks.indexOf(this.boundUpdateNavbar);
extraMenuUpdateCallbacks.splice(indexOfCallback, 1);
this.$target.css('top', '');
this.$target.find('.s_table_of_content_navbar').css({top: '', maxHeight: ''});
this._super(...arguments);
@@ -56,7 +56,17 @@ options.registry.TableOfContent = options.Class.extend({
$el.attr('id', id);
$el[0].dataset.anchor = 'true';
});
$nav.find('a:first').addClass('active');
const tocMainEl = this.$target[0].querySelector('.s_table_of_content_main');
if (tocMainEl && tocMainEl.children.length === 0) {
this.trigger_up('go_to_parent', {$snippet: this.$target});
// destroy public widget and remove the ToC since there are no more
// child elements.
this.trigger_up('will_remove_snippet', {$target: this.$target});
this.$target[0].remove();
} else {
$nav.find('a:first').addClass('active');
}
},
});