diff --git a/addons/account/models/account_move_line.py b/addons/account/models/account_move_line.py index e6d5916007e..2a32ce77ea6 100644 --- a/addons/account/models/account_move_line.py +++ b/addons/account/models/account_move_line.py @@ -1609,52 +1609,51 @@ class AccountMoveLine(models.Model): # ------------------------------------------------------------------------- @api.model - def _prepare_reconciliation_single_partial(self, debit_vals, credit_vals): + def _prepare_reconciliation_single_partial(self, debit_values, credit_values): """ Prepare the values to create an account.partial.reconcile later when reconciling the dictionaries passed as parameters, each one representing an account.move.line. - :param debit_vals: The values of account.move.line to consider for a debit line. - :param credit_vals: The values of account.move.line to consider for a credit line. + :param debit_values: The values of account.move.line to consider for a debit line. + :param credit_values: The values of account.move.line to consider for a credit line. :return: A dictionary: - * debit_vals: None if the line has nothing left to reconcile. - * credit_vals: None if the line has nothing left to reconcile. - * partial_vals: The newly computed values for the partial. + * debit_values: None if the line has nothing left to reconcile. + * credit_values: None if the line has nothing left to reconcile. + * partial_values: The newly computed values for the partial. + * exchange_values: The values to create an exchange difference linked to this partial. """ - def get_odoo_rate(vals): - if vals.get('record') and vals['record'].move_id.is_invoice(include_receipts=True): - exchange_rate_date = vals['record'].move_id.invoice_date + def get_odoo_rate(aml, currency): + if aml.move_id.is_invoice(include_receipts=True): + exchange_rate_date = aml.move_id.invoice_date else: - exchange_rate_date = vals['date'] - return recon_currency._get_conversion_rate(company_currency, recon_currency, vals['company'], exchange_rate_date) + exchange_rate_date = aml.date + return currency._get_conversion_rate(aml.company_currency_id, currency, aml.company_id, exchange_rate_date) - def get_accounting_rate(vals): - if company_currency.is_zero(vals['balance']) or vals['currency'].is_zero(vals['amount_currency']): - return None - else: - return abs(vals['amount_currency']) / abs(vals['balance']) + def get_accounting_rate(aml): + if not aml.company_currency_id.is_zero(aml.balance) and not aml.currency_id.is_zero(aml.amount_currency): + return abs(aml.amount_currency) / abs(aml.balance) # ==== Determine the currency in which the reconciliation will be done ==== # In this part, we retrieve the residual amounts, check if they are zero or not and determine in which # currency and at which rate the reconciliation will be done. - res = { - 'debit_vals': debit_vals, - 'credit_vals': credit_vals, + 'debit_values': debit_values, + 'credit_values': credit_values, } - remaining_debit_amount_curr = debit_vals['amount_residual_currency'] - remaining_credit_amount_curr = credit_vals['amount_residual_currency'] - remaining_debit_amount = debit_vals['amount_residual'] - remaining_credit_amount = credit_vals['amount_residual'] + remaining_debit_amount_curr = debit_values['amount_residual_currency'] + remaining_credit_amount_curr = credit_values['amount_residual_currency'] + remaining_debit_amount = debit_values['amount_residual'] + remaining_credit_amount = credit_values['amount_residual'] - company_currency = debit_vals['company'].currency_id + debit_aml = debit_values['aml'] + credit_aml = credit_values['aml'] + company_currency = debit_aml.company_currency_id has_debit_zero_residual = company_currency.is_zero(remaining_debit_amount) has_credit_zero_residual = company_currency.is_zero(remaining_credit_amount) - has_debit_zero_residual_currency = debit_vals['currency'].is_zero(remaining_debit_amount_curr) - has_credit_zero_residual_currency = credit_vals['currency'].is_zero(remaining_credit_amount_curr) - is_rec_pay_account = debit_vals.get('record') \ - and debit_vals['record'].account_type in ('asset_receivable', 'liability_payable') + has_debit_zero_residual_currency = debit_aml.currency_id.is_zero(remaining_debit_amount_curr) + has_credit_zero_residual_currency = credit_aml.currency_id.is_zero(remaining_credit_amount_curr) + is_rec_pay_account = debit_aml.account_type in ('asset_receivable', 'liability_payable') - if debit_vals['currency'] == credit_vals['currency'] == company_currency \ + if debit_aml.currency_id == credit_aml.currency_id == company_currency \ and not has_debit_zero_residual \ and not has_credit_zero_residual: # Everything is expressed in company's currency and there is something left to reconcile. @@ -1662,42 +1661,42 @@ class AccountMoveLine(models.Model): debit_rate = credit_rate = 1.0 recon_debit_amount = remaining_debit_amount recon_credit_amount = -remaining_credit_amount - elif debit_vals['currency'] == company_currency \ + elif debit_aml.currency_id == company_currency \ and is_rec_pay_account \ and not has_debit_zero_residual \ - and credit_vals['currency'] != company_currency \ + and credit_aml.currency_id != company_currency \ and not has_credit_zero_residual_currency: # The credit line is using a foreign currency but not the opposite line. # In that case, convert the amount in company currency to the foreign currency one. - recon_currency = credit_vals['currency'] - debit_rate = get_odoo_rate(debit_vals) - credit_rate = get_accounting_rate(credit_vals) + recon_currency = credit_aml.currency_id + debit_rate = get_odoo_rate(debit_aml, recon_currency) + credit_rate = get_accounting_rate(credit_aml) recon_debit_amount = recon_currency.round(remaining_debit_amount * debit_rate) recon_credit_amount = -remaining_credit_amount_curr - elif debit_vals['currency'] != company_currency \ + elif debit_aml.currency_id != company_currency \ and is_rec_pay_account \ and not has_debit_zero_residual_currency \ - and credit_vals['currency'] == company_currency \ + and credit_aml.currency_id == company_currency \ and not has_credit_zero_residual: # The debit line is using a foreign currency but not the opposite line. # In that case, convert the amount in company currency to the foreign currency one. - recon_currency = debit_vals['currency'] - debit_rate = get_accounting_rate(debit_vals) - credit_rate = get_odoo_rate(credit_vals) + recon_currency = debit_aml.currency_id + debit_rate = get_accounting_rate(debit_aml) + credit_rate = get_odoo_rate(credit_aml, recon_currency) recon_debit_amount = remaining_debit_amount_curr recon_credit_amount = recon_currency.round(-remaining_credit_amount * credit_rate) - elif debit_vals['currency'] == credit_vals['currency'] \ - and debit_vals['currency'] != company_currency \ + elif debit_aml.currency_id == credit_aml.currency_id \ + and debit_aml.currency_id != company_currency \ and not has_debit_zero_residual_currency \ and not has_credit_zero_residual_currency: # Both lines are sharing the same foreign currency. - recon_currency = debit_vals['currency'] - debit_rate = get_accounting_rate(debit_vals) - credit_rate = get_accounting_rate(credit_vals) + recon_currency = debit_aml.currency_id + debit_rate = get_accounting_rate(debit_aml) + credit_rate = get_accounting_rate(credit_aml) recon_debit_amount = remaining_debit_amount_curr recon_credit_amount = -remaining_credit_amount_curr - elif debit_vals['currency'] == credit_vals['currency'] \ - and debit_vals['currency'] != company_currency \ + elif debit_aml.currency_id == credit_aml.currency_id \ + and debit_aml.currency_id != company_currency \ and (has_debit_zero_residual_currency or has_credit_zero_residual_currency): # Special case for exchange difference lines. In that case, both lines are sharing the same foreign # currency but at least one has no amount in foreign currency. @@ -1711,18 +1710,18 @@ class AccountMoveLine(models.Model): else: # Multiple involved foreign currencies. The reconciliation is done using the currency of the company. recon_currency = company_currency - debit_rate = get_accounting_rate(debit_vals) - credit_rate = get_accounting_rate(credit_vals) + debit_rate = get_accounting_rate(debit_aml) + credit_rate = get_accounting_rate(credit_aml) recon_debit_amount = remaining_debit_amount recon_credit_amount = -remaining_credit_amount # Check if there is something left to reconcile. Move to the next loop iteration if not. skip_reconciliation = False if recon_currency.is_zero(recon_debit_amount): - res['debit_vals'] = None + res['debit_values'] = None skip_reconciliation = True if recon_currency.is_zero(recon_credit_amount): - res['credit_vals'] = None + res['credit_values'] = None skip_reconciliation = True if skip_reconciliation: return res @@ -1742,12 +1741,12 @@ class AccountMoveLine(models.Model): # Compute the partial amount expressed in foreign currency. if debit_rate: - partial_debit_amount_currency = debit_vals['currency'].round(debit_rate * min_recon_amount) + partial_debit_amount_currency = debit_aml.currency_id.round(debit_rate * min_recon_amount) partial_debit_amount_currency = min(partial_debit_amount_currency, remaining_debit_amount_curr) else: partial_debit_amount_currency = 0.0 if credit_rate: - partial_credit_amount_currency = credit_vals['currency'].round(credit_rate * min_recon_amount) + partial_credit_amount_currency = credit_aml.currency_id.round(credit_rate * min_recon_amount) partial_credit_amount_currency = min(partial_credit_amount_currency, -remaining_credit_amount_curr) else: partial_credit_amount_currency = 0.0 @@ -1770,11 +1769,11 @@ class AccountMoveLine(models.Model): # Compute the partial amount expressed in foreign currency. # Take care to handle the case when a line expressed in company currency is mimicking the foreign # currency of the opposite line. - if debit_vals['currency'] == company_currency: + if debit_aml.currency_id == company_currency: partial_debit_amount_currency = partial_amount else: partial_debit_amount_currency = min_recon_amount - if credit_vals['currency'] == company_currency: + if credit_aml.currency_id == company_currency: partial_credit_amount_currency = partial_amount else: partial_credit_amount_currency = min_recon_amount @@ -1787,16 +1786,14 @@ class AccountMoveLine(models.Model): if recon_currency == company_currency: if debit_fully_matched: debit_exchange_amount = remaining_debit_amount_curr - partial_debit_amount_currency - if not debit_vals['currency'].is_zero(debit_exchange_amount): - if debit_vals.get('record'): - exchange_lines_to_fix += debit_vals['record'] + if not debit_aml.currency_id.is_zero(debit_exchange_amount): + exchange_lines_to_fix += debit_aml amounts_list.append({'amount_residual_currency': debit_exchange_amount}) remaining_debit_amount_curr -= debit_exchange_amount if credit_fully_matched: credit_exchange_amount = remaining_credit_amount_curr + partial_credit_amount_currency - if not credit_vals['currency'].is_zero(credit_exchange_amount): - if credit_vals.get('record'): - exchange_lines_to_fix += credit_vals['record'] + if not credit_aml.currency_id.is_zero(credit_exchange_amount): + exchange_lines_to_fix += credit_aml amounts_list.append({'amount_residual_currency': credit_exchange_amount}) remaining_credit_amount_curr += credit_exchange_amount @@ -1805,11 +1802,10 @@ class AccountMoveLine(models.Model): # Create an exchange difference on the remaining amount expressed in company's currency. debit_exchange_amount = remaining_debit_amount - partial_amount if not company_currency.is_zero(debit_exchange_amount): - if debit_vals.get('record'): - exchange_lines_to_fix += debit_vals['record'] + exchange_lines_to_fix += debit_aml amounts_list.append({'amount_residual': debit_exchange_amount}) remaining_debit_amount -= debit_exchange_amount - if debit_vals['currency'] == company_currency: + if debit_aml.currency_id == company_currency: remaining_debit_amount_curr -= debit_exchange_amount else: # Create an exchange difference ensuring the rate between the residual amounts expressed in @@ -1817,22 +1813,20 @@ class AccountMoveLine(models.Model): # 'amount_currency' & 'balance'. debit_exchange_amount = partial_debit_amount - partial_amount if company_currency.compare_amounts(debit_exchange_amount, 0.0) > 0: - if debit_vals.get('record'): - exchange_lines_to_fix += debit_vals['record'] + exchange_lines_to_fix += debit_aml amounts_list.append({'amount_residual': debit_exchange_amount}) remaining_debit_amount -= debit_exchange_amount - if debit_vals['currency'] == company_currency: + if debit_aml.currency_id == company_currency: remaining_debit_amount_curr -= debit_exchange_amount if credit_fully_matched: # Create an exchange difference on the remaining amount expressed in company's currency. credit_exchange_amount = remaining_credit_amount + partial_amount if not company_currency.is_zero(credit_exchange_amount): - if credit_vals.get('record'): - exchange_lines_to_fix += credit_vals['record'] + exchange_lines_to_fix += credit_aml amounts_list.append({'amount_residual': credit_exchange_amount}) remaining_credit_amount += credit_exchange_amount - if credit_vals['currency'] == company_currency: + if credit_aml.currency_id == company_currency: remaining_credit_amount_curr -= credit_exchange_amount else: # Create an exchange difference ensuring the rate between the residual amounts expressed in @@ -1840,17 +1834,16 @@ class AccountMoveLine(models.Model): # 'amount_currency' & 'balance'. credit_exchange_amount = partial_amount - partial_credit_amount if company_currency.compare_amounts(credit_exchange_amount, 0.0) < 0: - if credit_vals.get('record'): - exchange_lines_to_fix += credit_vals['record'] + exchange_lines_to_fix += credit_aml amounts_list.append({'amount_residual': credit_exchange_amount}) remaining_credit_amount -= credit_exchange_amount - if credit_vals['currency'] == company_currency: + if credit_aml.currency_id == company_currency: remaining_credit_amount_curr -= credit_exchange_amount if exchange_lines_to_fix: - res['exchange_vals'] = exchange_lines_to_fix._prepare_exchange_difference_move_vals( + res['exchange_values'] = exchange_lines_to_fix._prepare_exchange_difference_move_vals( amounts_list, - exchange_date=max(debit_vals['date'], credit_vals['date']), + exchange_date=max(debit_aml.date, credit_aml.date), ) # ==== Create partials ==== @@ -1860,71 +1853,464 @@ class AccountMoveLine(models.Model): remaining_debit_amount_curr -= partial_debit_amount_currency remaining_credit_amount_curr += partial_credit_amount_currency - res['partial_vals'] = { + res['partial_values'] = { 'amount': partial_amount, 'debit_amount_currency': partial_debit_amount_currency, 'credit_amount_currency': partial_credit_amount_currency, - 'debit_move_id': debit_vals.get('record') and debit_vals['record'].id, - 'credit_move_id': credit_vals.get('record') and credit_vals['record'].id, + 'debit_move_id': debit_aml.id, + 'credit_move_id': credit_aml.id, } - debit_vals['amount_residual'] = remaining_debit_amount - debit_vals['amount_residual_currency'] = remaining_debit_amount_curr - credit_vals['amount_residual'] = remaining_credit_amount - credit_vals['amount_residual_currency'] = remaining_credit_amount_curr + debit_values['amount_residual'] = remaining_debit_amount + debit_values['amount_residual_currency'] = remaining_debit_amount_curr + credit_values['amount_residual'] = remaining_credit_amount + credit_values['amount_residual_currency'] = remaining_credit_amount_curr if debit_fully_matched: - res['debit_vals'] = None + res['debit_values'] = None if credit_fully_matched: - res['credit_vals'] = None + res['credit_values'] = None return res @api.model - def _prepare_reconciliation_partials(self, vals_list): - ''' Prepare the partials on the current journal items to perform the reconciliation. + def _prepare_reconciliation_amls(self, values_list): + """ Prepare the partials on the current journal items to perform the reconciliation. Note: The order of records in self is important because the journal items will be reconciled using this order. - :return: a tuple of 1) list of vals for partial reconciliation creation, 2) the list of vals for the exchange difference entries to be created - ''' - debit_vals_list = iter([x for x in vals_list if x['balance'] > 0.0 or x['amount_currency'] > 0.0]) - credit_vals_list = iter([x for x in vals_list if x['balance'] < 0.0 or x['amount_currency'] < 0.0]) - debit_vals = None - credit_vals = None - partials_vals_list = [] - exchange_data = {} + :return: a tuple of + 1) list of vals for partial reconciliation creation, + 2) the list of vals for the exchange difference entries to be created + """ + debit_values_list = iter([x for x in values_list if x['aml'].balance > 0.0 or x['aml'].amount_currency > 0.0]) + credit_values_list = iter([x for x in values_list if x['aml'].balance < 0.0 or x['aml'].amount_currency < 0.0]) + debit_values = None + credit_values = None + fully_reconciled_aml_ids = set() + all_results = [] while True: # ==== Find the next available lines ==== # For performance reasons, the partials are created all at once meaning the residual amounts can't be # trusted from one iteration to another. That's the reason why all residual amounts are kept as variables - # and reduced "manually" every time we append a dictionary to 'partials_vals_list'. + # and reduced "manually" every time we append a dictionary to 'partials_values_list'. # Move to the next available debit line. - if not debit_vals: - debit_vals = next(debit_vals_list, None) - if not debit_vals: + if not debit_values: + debit_values = next(debit_values_list, None) + if not debit_values: break # Move to the next available credit line. - if not credit_vals: - credit_vals = next(credit_vals_list, None) - if not credit_vals: + if not credit_values: + credit_values = next(credit_values_list, None) + if not credit_values: break # ==== Compute the amounts to reconcile ==== - res = self._prepare_reconciliation_single_partial(debit_vals, credit_vals) - if res.get('partial_vals'): - if res.get('exchange_vals'): - exchange_data[len(partials_vals_list)] = res['exchange_vals'] - partials_vals_list.append(res['partial_vals']) - if res['debit_vals'] is None: - debit_vals = None - if res['credit_vals'] is None: - credit_vals = None + results = self._prepare_reconciliation_single_partial(debit_values, credit_values) + if results.get('partial_values'): + all_results.append(results) + if results['debit_values'] is None: + fully_reconciled_aml_ids.add(debit_values['aml'].id) + debit_values = None + if results['credit_values'] is None: + fully_reconciled_aml_ids.add(credit_values['aml'].id) + credit_values = None - return partials_vals_list, exchange_data + return all_results, fully_reconciled_aml_ids + + @api.model + def _prepare_reconciliation_plan(self, plan, amls_values_map): + """ Perform virtually the reconciliation of the plan passed as parameter. + + :param plan: The plan to know which lines to reconcile in which order. + :param amls_values_map: A mapping aml => amount_residual/amount_residual_currency + :return: A list of all results returned by the '_prepare_reconciliation_amls' method. + """ + all_fully_reconciled_aml_ids = set() + all_results = [] + + def process_amls(amls): + remaining_amls = amls.filtered(lambda aml: aml.id not in all_fully_reconciled_aml_ids) + amls_results, fully_reconciled_aml_ids = self._prepare_reconciliation_amls([ + amls_values_map[aml] + for aml in remaining_amls + ]) + all_fully_reconciled_aml_ids.update(fully_reconciled_aml_ids) + for amls_result in amls_results: + all_results.append(amls_result) + + def process_leaf(plan_node): + # Sub plan to evaluate. + for child_node in plan_node.get('nodes', []): + process_leaf(child_node) + + # Group of amls to evaluate. + process_amls(plan_node['amls']) + + process_leaf(plan) + return all_results + + @api.model + def _optimize_reconciliation_plan(self, reconciliation_plan): + """ Decode the initial reconciliation plan passed as parameter and converted it into a list of tree depicting + the way the reconciliation should be done. + Also, this method is responsible sorting the amls and splitting them by currency. + Then, this method checks the parameter to ensure we are not going to perform any invalid reconciliation like + a cross-account/cross-company partial. + + The split by currencies is made as follows. + Suppose account.move.line(1, 2) are expressed in currency1 and account.move.line(3, 4) are expressed + in currency2. + If the reconciliation plan is [account.move.line(1, 2, 3, 4)], the optimizer will convert it into: + [[account.move.line(1, 2), account.move.line(3, 4)]] + + :param reconciliation_plan: A list of reconciliation to perform. + :return: A list of dictionaries containing: + * amls: A recordset. + * aml_ids: The recordset ids. + * nodes: A list of sub-nodes. + """ + + def process_amls(amls): + sorted_amls = amls.sorted(key=lambda aml: ( + aml.date_maturity or aml.date, + aml.currency_id, + aml.amount_currency, + aml.balance, + )) + currencies = sorted_amls.currency_id + + results = { + 'amls': sorted_amls, + 'aml_ids': set(sorted_amls.ids), + } + + if len(currencies) != 1: + nodes = results['nodes'] = [] + for currency in currencies: + amls_in_currency = sorted_amls.filtered(lambda x: x.currency_id == currency) + nodes.append({ + 'amls': amls_in_currency, + 'aml_ids': set(amls_in_currency.ids), + }) + return results + + def process_children(children): + node = { + 'nodes': [], + 'aml_ids': set(), + } + for child in children: + results = process_leaf(child) + if results: + node['nodes'].append(results) + node['aml_ids'].update(results['aml_ids']) + node['amls'] = self.browse(node['aml_ids']) + return node + + def process_leaf(item): + if not item: + return + + if isinstance(item, models.BaseModel): + # Group of amls to evaluate. + return process_amls(item) + else: + # Sub plan to evaluate. + return process_children(item) + + plan_list = [] + all_aml_ids = set() + for item in reconciliation_plan: + plan_node = process_leaf(item) + if not plan_node or not plan_node.get('amls'): + continue + + # Check the amls to be reconciled all together. + amls = plan_node['amls'] + if any(aml.reconciled for aml in amls): + raise UserError(_("You are trying to reconcile some entries that are already reconciled.")) + if any(aml.parent_state != 'posted' for aml in amls): + raise UserError(_("You can only reconcile posted entries.")) + if len(amls.account_id) > 1: + raise UserError(_( + "Entries are not from the same account: %s", + ", ".join(amls.account_id.mapped('display_name')), + )) + if len(amls.company_id) > 1: + raise UserError(_( + "Entries doesn't belong to the same company: %s", + ", ".join(amls.company_id.mapped('display_name')), + )) + + if any( + not aml.account_id.reconcile + and aml.account_id.account_type not in ('asset_cash', 'liability_credit_card') + for aml in amls + ): + raise UserError(_( + "Account %s does not allow reconciliation. First change the configuration of this account " + "to allow it.", + amls.account_id.display_name + )) + + plan_list.append(plan_node) + all_aml_ids.update(plan_node['aml_ids']) + + return plan_list, self.browse(all_aml_ids) + + def _reconcile_pre_hook(self): + not_paid_invoices = self.move_id.filtered(lambda move: + move.is_invoice(include_receipts=True) + and move.payment_state not in ('paid', 'in_payment') + ) + return {'not_paid_invoices': not_paid_invoices} + + def _reconcile_post_hook(self, data): + data['not_paid_invoices']\ + .filtered(lambda move: move.payment_state in ('paid', 'in_payment'))\ + ._invoice_paid_hook() + + @api.model + def _reconcile_plan(self, reconciliation_plan): + """ Reconcile the amls following the reconciliation plan. + The plan passed as parameter is a list of either a recordset of amls, either another plan. + + For example: + [account.move.line(1, 2), account.move.line(3, 4)] means: + - account.move.line(1, 2) will be reconciled first. + - account.move.line(3, 4) will be reconciled after. + + [[account.move.line(1, 2), account.move.line(3, 4)]] means: + - account.move.line(1, 2) will be reconciled first. + - account.move.line(3, 4) will be reconciled after. + - account.move.line(1, 2, 3, 4).filtered(lambda x: not x.reconciled) will be reconciled at the end. + + :param reconciliation_plan: A list of reconciliation to perform. + """ + # Parameter allowing to disable the exchange journal entries on partials. + disable_partial_exchange_diff = bool(self.env['ir.config_parameter'].sudo().get_param('account.disable_partial_exchange_diff')) + + # ==== Prepare the reconciliation ==== + # Batch the amls all together to know what should be reconciled and when. + plan_list, all_amls = self._optimize_reconciliation_plan(reconciliation_plan) + + # ==== Prefetch the fields all at once to speedup the reconciliation ==== + # All of those fields will be cached by the orm. Since the amls are split into multiple batches, the orm is not + # able to prefetch the data for all of them at once. For that reason, we force the orm to populate the cache + # before doing anything. + all_amls.move_id + all_amls.matched_debit_ids + all_amls.matched_credit_ids + + # ==== Track the invoice's state to call the hook when they become paid ==== + pre_hook_data = all_amls._reconcile_pre_hook() + + # ==== Collect amls data ==== + # All residual amounts are collected and updated until the creation of partials in batch. + # This is done that way to minimize the orm time for fields invalidation/mark as recompute and + # recomputation. + aml_values_map = { + aml: { + 'aml': aml, + 'amount_residual': aml.amount_residual, + 'amount_residual_currency': aml.amount_residual_currency, + } + for aml in all_amls + } + + # ==== Prepare the partials ==== + partials_values_list = [] + exchange_diff_values_list = [] + exchange_diff_partial_index = [] + all_plan_results = [] + partial_index = 0 + for plan in plan_list: + plan_results = self\ + .with_context(no_exchange_difference=self._context.get('no_exchange_difference') or disable_partial_exchange_diff)\ + ._prepare_reconciliation_plan(plan, aml_values_map) + all_plan_results.append(plan_results) + for results in plan_results: + partials_values_list.append(results['partial_values']) + if results.get('exchange_values') and results['exchange_values']['move_values']['line_ids']: + exchange_diff_values_list.append(results['exchange_values']) + exchange_diff_partial_index.append(partial_index) + partial_index += 1 + + # ==== Create the partials ==== + # Link the newly created partials to the plan. There are needed later for caba exchange entries. + partials = self.env['account.partial.reconcile'].create(partials_values_list) + start_range = 0 + for plan_results, plan in zip(all_plan_results, plan_list): + size = len(plan_results) + plan['partials'] = partials[start_range:start_range + size] + start_range += size + + # ==== Create the partial exchange journal entries ==== + exchange_moves = self._create_exchange_difference_moves(exchange_diff_values_list) + for index, exchange_move in zip(exchange_diff_partial_index, exchange_moves): + partials[index].exchange_move_id = exchange_move + + # ==== Create entries for cash basis taxes ==== + def is_cash_basis_needed(account): + return account.company_id.tax_exigibility \ + and account.account_type in ('asset_receivable', 'liability_payable') + + if not self._context.get('move_reverse_cancel'): + for plan in plan_list: + if is_cash_basis_needed(plan['amls'].account_id): + plan['partials']._create_tax_cash_basis_moves() + + # ==== Prepare full reconcile creation ==== + # First, we need to find all sub-set of amls that are candidates for a full. + + def is_line_reconciled(aml, has_multiple_currencies): + # Check if the journal item passed as parameter is now fully reconciled. + if aml.reconciled: + return True + if not aml.matched_debit_ids and not aml.matched_credit_ids: + # Suppose a journal item having balance = 0 but an amount_currency like an exchange difference. + return False + if has_multiple_currencies: + return aml.company_currency_id.is_zero(aml.amount_residual) + else: + return aml.currency_id.is_zero(aml.amount_residual_currency) + + full_batches = [] + all_aml_ids = set() + for plan in plan_list: + for aml in plan['amls']: + if 'full_batch_index' in aml_values_map[aml]: + continue + + involved_amls = plan['amls']._all_reconciled_lines() + all_aml_ids.update(involved_amls.ids) + full_batch_index = len(full_batches) + has_multiple_currencies = len(involved_amls.currency_id) > 1 + is_fully_reconciled = all( + is_line_reconciled(involved_aml, has_multiple_currencies) + for involved_aml in involved_amls + ) + full_batches.append({ + 'amls': involved_amls, + 'is_fully_reconciled': is_fully_reconciled, + }) + for involved_aml in involved_amls: + if aml_values_map.get(involved_aml): + aml_values_map[involved_aml]['full_batch_index'] = full_batch_index + + # ==== Prefetch the fields all at once to speedup the reconciliation ==== + # Again, we do the same optimization for the prefetching. We need to do it again since most of the values have + # been invalidated with the creation of the account.partial.reconcile records. + all_amls = self.browse(list(all_aml_ids)) + all_amls.move_id + all_amls.matched_debit_ids + all_amls.matched_credit_ids + + # ==== Prepare the full exchange journal entries ==== + # This part could be bypassed using the 'no_exchange_difference' key inside the context. This is useful + # when importing a full accounting including the reconciliation like Winbooks. + + exchange_diff_values_list = [] + exchange_diff_full_batch_index = [] + if not self._context.get('no_exchange_difference'): + for fulL_batch_index, full_batch in enumerate(full_batches): + involved_amls = full_batch['amls'] + if not full_batch['is_fully_reconciled']: + continue + + # In normal cases, the exchange differences are already generated by the partial at this point meaning + # there is no journal item left with a zero amount residual in one currency but not in the other. + # However, after a migration coming from an older version with an older partial reconciliation or due to + # some rounding issues (when dealing with different decimal places for example), we could need an extra + # exchange difference journal entry to handle them. + exchange_lines_to_fix = self.env['account.move.line'] + amounts_list = [] + exchange_max_date = date.min + for aml in involved_amls: + if not aml.company_currency_id.is_zero(aml.amount_residual): + exchange_lines_to_fix += aml + amounts_list.append({'amount_residual': aml.amount_residual}) + elif not aml.currency_id.is_zero(aml.amount_residual_currency): + exchange_lines_to_fix += aml + amounts_list.append({'amount_residual_currency': aml.amount_residual_currency}) + exchange_max_date = max(exchange_max_date, aml.date) + exchange_diff_values = exchange_lines_to_fix._prepare_exchange_difference_move_vals( + amounts_list, + company=involved_amls.company_id, + exchange_date=exchange_max_date, + ) + + # Exchange difference for cash basis entries. + caba_lines_to_reconcile = None + if is_cash_basis_needed(involved_amls.account_id): + caba_lines_to_reconcile = involved_amls._add_exchange_difference_cash_basis_vals(exchange_diff_values) + + # Prepare the exchange difference. + if exchange_diff_values['move_values']['line_ids']: + exchange_diff_full_batch_index.append(fulL_batch_index) + exchange_diff_values_list.append(exchange_diff_values) + full_batch['caba_lines_to_reconcile'] = caba_lines_to_reconcile + + # ==== Create the full exchange journal entries ==== + exchange_moves = self._create_exchange_difference_moves(exchange_diff_values_list) + for fulL_batch_index, exchange_move in zip(exchange_diff_full_batch_index, exchange_moves): + full_batch = full_batches[full_batch_index] + amls = full_batch['amls'] + full_batch['exchange_move'] = exchange_move + exchange_move_lines = exchange_move.line_ids.filtered(lambda line: line.account_id == amls.account_id) + full_batch['amls'] |= exchange_move_lines + + # ==== Create the full reconcile ==== + # Note we are using Command.link and not Command.set because Command.set is triggering an unlink that is + # slowing down the assignation of the co-fields. Indeed, unlink is forcing a flush. + full_reconcile_values_list = [] + full_reconcile_fulL_batch_index = [] + for fulL_batch_index, full_batch in enumerate(full_batches): + amls = full_batch['amls'] + involved_partials = amls.matched_debit_ids + amls.matched_credit_ids + if full_batch['is_fully_reconciled']: + full_reconcile_values_list.append({ + 'exchange_move_id': full_batch.get('exchange_move') and full_batch['exchange_move'].id, + 'partial_reconcile_ids': [Command.link(partial.id) for partial in involved_partials], + 'reconciled_line_ids': [Command.link(aml.id) for aml in amls], + }) + full_reconcile_fulL_batch_index.append(fulL_batch_index) + + self.env['account.full.reconcile']\ + .with_context( + skip_invoice_sync=True, + skip_invoice_line_sync=True, + skip_account_move_synchronization=True, + check_move_validity=False, + )\ + .create(full_reconcile_values_list) + + # === Cash basis rounding autoreconciliation === + # In case a cash basis rounding difference line got created for the transition account, we reconcile it with the corresponding lines + # on the cash basis moves (so that it reaches full reconciliation and creates an exchange difference entry for this account as well) + for fulL_batch_index, full_batch in enumerate(full_batches): + if not full_batch.get('caba_lines_to_reconcile'): + continue + + caba_lines_to_reconcile = full_batch['caba_lines_to_reconcile'] + exchange_move = full_batch['exchange_move'] + for (dummy, account, repartition_line), amls_to_reconcile in caba_lines_to_reconcile.items(): + if not account.reconcile: + continue + + exchange_line = exchange_move.line_ids.filtered( + lambda l: l.account_id == account and l.tax_repartition_line_id == repartition_line + ) + + (exchange_line + amls_to_reconcile)\ + .filtered(lambda l: not l.reconciled)\ + .reconcile() + + all_amls._reconcile_post_hook(pre_hook_data) def _create_reconciliation_partials(self): '''create the partial reconciliation between all the records in self @@ -1932,22 +2318,17 @@ class AccountMoveLine(models.Model): ''' partials_vals_list, exchange_data = self._prepare_reconciliation_partials([ { - 'record': line, - 'balance': line.balance, - 'amount_currency': line.amount_currency, + 'aml': line, 'amount_residual': line.amount_residual, 'amount_residual_currency': line.amount_residual_currency, - 'company': line.company_id, - 'currency': line.currency_id, - 'date': line.date, } for line in self ]) partials = self.env['account.partial.reconcile'].create(partials_vals_list) # ==== Create exchange difference moves ==== - for index, exchange_vals in exchange_data.items(): - partials[index].exchange_move_id = self._create_exchange_difference_move(exchange_vals) + for index, exchange_values in exchange_data.items(): + partials[index].exchange_move_id = self._create_exchange_difference_move(exchange_values) return partials @@ -2030,47 +2411,69 @@ class AccountMoveLine(models.Model): ] to_reconcile.append((line, sequence)) - return {'move_vals': move_vals, 'to_reconcile': to_reconcile} + return {'move_values': move_vals, 'to_reconcile': to_reconcile} @api.model - def _create_exchange_difference_move(self, exchange_diff_vals): + def _create_exchange_difference_moves(self, exchange_diff_values_list): """ Create the exchange difference journal entry on the current journal items. - :param exchange_diff_vals: The current vals of the exchange difference journal entry created by the - '_prepare_exchange_difference_move_vals' method. - :return: An account.move record. + + :param exchange_diff_values_list: A list of values to create and reconcile the exchange differences + See the '_prepare_exchange_difference_move_vals' method. + :return: An account.move recordset. """ - move_vals = exchange_diff_vals['move_vals'] - if not move_vals['line_ids']: - return + exchange_move_values_list = [] + journal_ids = set() + for exchange_diff_values in exchange_diff_values_list: + move_vals = exchange_diff_values['move_values'] + exchange_move_values_list.append(move_vals) - # Check the configuration of the exchange difference journal. - journal = self.env['account.journal'].browse(move_vals['journal_id']) - if not journal: - raise UserError(_( - "You should configure the 'Exchange Gain or Loss Journal' in your company settings, to manage" - " automatically the booking of accounting entries related to differences between exchange rates." - )) - if not journal.company_id.expense_currency_exchange_account_id: - raise UserError(_( - "You should configure the 'Loss Exchange Rate Account' in your company settings, to manage" - " automatically the booking of accounting entries related to differences between exchange rates." - )) - if not journal.company_id.income_currency_exchange_account_id.id: - raise UserError(_( - "You should configure the 'Gain Exchange Rate Account' in your company settings, to manage" - " automatically the booking of accounting entries related to differences between exchange rates." - )) + if not move_vals['journal_id']: + raise UserError(_( + "You have to configure the 'Exchange Gain or Loss Journal' in your company settings, to manage" + " automatically the booking of accounting entries related to differences between exchange rates." + )) - # Create the move. - exchange_move = self.env['account.move'].with_context(skip_invoice_sync=True).create(move_vals) - exchange_move._post(soft=False) + journal_ids.add(move_vals['journal_id']) - # Reconcile lines to the newly created exchange difference journal entry by creating more partials. - for source_line, sequence in exchange_diff_vals['to_reconcile']: - exchange_diff_line = exchange_move.line_ids[sequence] - (exchange_diff_line + source_line).with_context(no_exchange_difference=True).reconcile() + if not exchange_move_values_list: + return self.env['account.move'] - return exchange_move + # ==== Check the config ==== + journals = self.env['account.journal'].browse(list(journal_ids)) + for journal in journals: + if not journal.company_id.expense_currency_exchange_account_id: + raise UserError(_( + "You should configure the 'Loss Exchange Rate Account' in your company settings, to manage" + " automatically the booking of accounting entries related to differences between exchange rates." + )) + if not journal.company_id.income_currency_exchange_account_id.id: + raise UserError(_( + "You should configure the 'Gain Exchange Rate Account' in your company settings, to manage" + " automatically the booking of accounting entries related to differences between exchange rates." + )) + + # ==== Create the move ==== + exchange_moves = self.env['account.move']\ + .with_context( + skip_invoice_sync=True, + skip_invoice_line_sync=True, + skip_account_move_synchronization=True, + )\ + .create(exchange_move_values_list) + exchange_moves._post(soft=False) + + # ==== Reconcile ==== + reconciliation_plan = [] + for exchange_move, exchange_diff_values in zip(exchange_moves, exchange_diff_values_list): + for source_line, sequence in exchange_diff_values['to_reconcile']: + exchange_diff_line = exchange_move.line_ids[sequence] + reconciliation_plan.append((source_line + exchange_diff_line)) + + self\ + .with_context(no_exchange_difference=True)\ + ._reconcile_plan(reconciliation_plan) + + return exchange_moves def _add_exchange_difference_cash_basis_vals(self, exchange_diff_vals): """ Generate the exchange difference values used to create the journal items @@ -2089,7 +2492,7 @@ class AccountMoveLine(models.Model): '_prepare_exchange_difference_move_vals' method. """ caba_lines_to_reconcile = defaultdict(lambda: self.env['account.move.line']) # in the form {(move, account, repartition_line): move_lines} - move_vals = exchange_diff_vals['move_vals'] + move_vals = exchange_diff_vals['move_values'] for move in self.move_id: account_vals_to_fix = {} @@ -2278,168 +2681,8 @@ class AccountMoveLine(models.Model): return caba_lines_to_reconcile def reconcile(self): - ''' Reconcile the current move lines all together. - :return: A dictionary representing a summary of what has been done during the reconciliation: - * partials: A recorset of all account.partial.reconcile created during the reconciliation. - * exchange_partials: A recorset of all account.partial.reconcile created during the reconciliation - with the exchange difference journal entries. - * full_reconcile: An account.full.reconcile record created when there is nothing left to reconcile - in the involved lines. - * tax_cash_basis_moves: An account.move recordset representing the tax cash basis journal entries. - ''' - results = {'exchange_partials': self.env['account.partial.reconcile']} - - if not self: - return results - - not_paid_invoices = self.move_id.filtered(lambda move: - move.is_invoice(include_receipts=True) - and move.payment_state not in ('paid', 'in_payment') - ) - - # ==== Check the lines can be reconciled together ==== - company = None - account = None - for line in self: - if line.reconciled: - raise UserError(_("You are trying to reconcile some entries that are already reconciled.")) - if not line.account_id.reconcile and line.account_id.account_type not in ('asset_cash', 'liability_credit_card'): - raise UserError(_("Account %s does not allow reconciliation. First change the configuration of this account to allow it.") - % line.account_id.display_name) - if line.move_id.state != 'posted': - raise UserError(_('You can only reconcile posted entries.')) - if company is None: - company = line.company_id - elif line.company_id != company: - raise UserError(_("Entries doesn't belong to the same company: %s != %s") - % (company.display_name, line.company_id.display_name)) - if account is None: - account = line.account_id - elif line.account_id != account: - raise UserError(_("Entries are not from the same account: %s != %s") - % (account.display_name, line.account_id.display_name)) - - sorted_lines = self.sorted(key=lambda line: (line.date_maturity or line.date, line.currency_id, line.amount_currency)) - - # ==== Collect all involved lines through the existing reconciliation ==== - - involved_lines = sorted_lines._all_reconciled_lines() - involved_partials = involved_lines.matched_credit_ids | involved_lines.matched_debit_ids - - # ==== Create partials ==== - - partial_no_exch_diff = bool(self.env['ir.config_parameter'].sudo().get_param('account.disable_partial_exchange_diff')) - sorted_lines_ctx = sorted_lines.with_context(no_exchange_difference=self._context.get('no_exchange_difference') or partial_no_exch_diff) - partials = sorted_lines_ctx._create_reconciliation_partials() - results['partials'] = partials - involved_partials += partials - exchange_move_lines = partials.exchange_move_id.line_ids.filtered(lambda line: line.account_id == account) - involved_lines += exchange_move_lines - exchange_diff_partials = exchange_move_lines.matched_debit_ids + exchange_move_lines.matched_credit_ids - involved_partials += exchange_diff_partials - results['exchange_partials'] += exchange_diff_partials - - # ==== Create entries for cash basis taxes ==== - - is_cash_basis_needed = account.company_id.tax_exigibility and account.account_type in ('asset_receivable', 'liability_payable') - if is_cash_basis_needed and not self._context.get('move_reverse_cancel'): - tax_cash_basis_moves = partials._create_tax_cash_basis_moves() - results['tax_cash_basis_moves'] = tax_cash_basis_moves - - # ==== Check if a full reconcile is needed ==== - - def is_line_reconciled(line, has_multiple_currencies): - # Check if the journal item passed as parameter is now fully reconciled. - return line.reconciled \ - or (line.company_currency_id.is_zero(line.amount_residual) - if has_multiple_currencies - else line.currency_id.is_zero(line.amount_residual_currency) - ) - - has_multiple_currencies = len(involved_lines.currency_id) > 1 - if all(is_line_reconciled(line, has_multiple_currencies) for line in involved_lines): - # ==== Create the exchange difference move ==== - # This part could be bypassed using the 'no_exchange_difference' key inside the context. This is useful - # when importing a full accounting including the reconciliation like Winbooks. - - exchange_move = self.env['account.move'] - caba_lines_to_reconcile = None - if not self._context.get('no_exchange_difference'): - # In normal cases, the exchange differences are already generated by the partial at this point meaning - # there is no journal item left with a zero amount residual in one currency but not in the other. - # However, after a migration coming from an older version with an older partial reconciliation or due to - # some rounding issues (when dealing with different decimal places for example), we could need an extra - # exchange difference journal entry to handle them. - exchange_lines_to_fix = self.env['account.move.line'] - amounts_list = [] - exchange_max_date = date.min - for line in involved_lines: - if not line.company_currency_id.is_zero(line.amount_residual): - exchange_lines_to_fix += line - amounts_list.append({'amount_residual': line.amount_residual}) - elif not line.currency_id.is_zero(line.amount_residual_currency): - exchange_lines_to_fix += line - amounts_list.append({'amount_residual_currency': line.amount_residual_currency}) - exchange_max_date = max(exchange_max_date, line.date) - exchange_diff_vals = exchange_lines_to_fix._prepare_exchange_difference_move_vals( - amounts_list, - company=involved_lines[0].company_id, - exchange_date=exchange_max_date, - ) - - # Exchange difference for cash basis entries. - if is_cash_basis_needed: - caba_lines_to_reconcile = involved_lines._add_exchange_difference_cash_basis_vals(exchange_diff_vals) - - # Create the exchange difference. - if exchange_diff_vals['move_vals']['line_ids']: - exchange_move = involved_lines._create_exchange_difference_move(exchange_diff_vals) - if exchange_move: - exchange_move_lines = exchange_move.line_ids.filtered(lambda line: line.account_id == account) - - # Track newly created lines. - involved_lines += exchange_move_lines - - # Track newly created partials. - exchange_diff_partials = exchange_move_lines.matched_debit_ids \ - + exchange_move_lines.matched_credit_ids - involved_partials += exchange_diff_partials - results['exchange_partials'] += exchange_diff_partials - - # ==== Create the full reconcile ==== - results['full_reconcile'] = self.env['account.full.reconcile'] \ - .with_context( - skip_invoice_sync=True, - skip_invoice_line_sync=True, - skip_account_move_synchronization=True, - check_move_validity=False, - ) \ - .create({ - 'exchange_move_id': exchange_move and exchange_move.id, - 'partial_reconcile_ids': [Command.set(involved_partials.ids)], - 'reconciled_line_ids': [Command.set(involved_lines.ids)], - }) - - # === Cash basis rounding autoreconciliation === - # In case a cash basis rounding difference line got created for the transition account, we reconcile it with the corresponding lines - # on the cash basis moves (so that it reaches full reconciliation and creates an exchange difference entry for this account as well) - - if caba_lines_to_reconcile: - for (dummy, account, repartition_line), amls_to_reconcile in caba_lines_to_reconcile.items(): - if not account.reconcile: - continue - - exchange_line = exchange_move.line_ids.filtered( - lambda l: l.account_id == account and l.tax_repartition_line_id == repartition_line - ) - - (exchange_line + amls_to_reconcile).filtered(lambda l: not l.reconciled).reconcile() - - not_paid_invoices.filtered(lambda move: - move.payment_state in ('paid', 'in_payment') - )._invoice_paid_hook() - - return results + """ Reconcile the current move lines all together. """ + return self._reconcile_plan([self]) def remove_move_reconcile(self): """ Undo a reconciliation """ diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index bb5a74c5b7d..df235667645 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -520,10 +520,17 @@ class AccountPartialReconcile(models.Model): moves_to_create.append(move_vals) - moves = self.env['account.move'].create(moves_to_create) + moves = self.env['account.move']\ + .with_context( + skip_invoice_sync=True, + skip_invoice_line_sync=True, + skip_account_move_synchronization=True, + )\ + .create(moves_to_create) moves._post(soft=False) # Reconcile the tax lines being on a reconcile tax basis transfer account. + reconciliation_plan = [] for lines, move_index, sequence in to_reconcile_after: # In expenses, all move lines are created manually without any grouping on tax lines. @@ -538,6 +545,8 @@ class AccountPartialReconcile(models.Model): if counterpart_line.reconciled: continue - (lines + counterpart_line).reconcile() + reconciliation_plan.append((counterpart_line + lines)) + + self.env['account.move.line']._reconcile_plan(reconciliation_plan) return moves diff --git a/addons/account/tests/test_account_move_date_algorithm.py b/addons/account/tests/test_account_move_date_algorithm.py index 0d84634f76f..c06ec23b45e 100644 --- a/addons/account/tests/test_account_move_date_algorithm.py +++ b/addons/account/tests/test_account_move_date_algorithm.py @@ -151,10 +151,9 @@ class TestAccountMoveDateAlgorithm(AccountTestInvoicingCommon): (invoice + refund).action_post() self._set_lock_date('2017-01-31') - res = (invoice + refund).line_ids\ - .filtered(lambda x: x.account_id.account_type == 'asset_receivable')\ - .reconcile() - exchange_move = res['partials'].exchange_move_id + amls = (invoice + refund).line_ids.filtered(lambda x: x.account_id.account_type == 'asset_receivable') + amls.reconcile() + exchange_move = amls.matched_debit_ids.exchange_move_id self.assertRecordValues(exchange_move, [{ 'date': fields.Date.from_string('2017-02-01'), @@ -167,10 +166,9 @@ class TestAccountMoveDateAlgorithm(AccountTestInvoicingCommon): refund = self._create_invoice('out_refund', '2017-01-01', currency_id=self.currency_data['currency'].id) (invoice + refund).action_post() - res = (invoice + refund).line_ids\ - .filtered(lambda x: x.account_id.account_type == 'asset_receivable')\ - .reconcile() - exchange_move = res['partials'].exchange_move_id + amls = (invoice + refund).line_ids.filtered(lambda x: x.account_id.account_type == 'asset_receivable') + amls.reconcile() + exchange_move = amls.matched_debit_ids.exchange_move_id self._set_lock_date('2017-01-31') (invoice + refund).line_ids.remove_move_reconcile() diff --git a/addons/account/tests/test_account_move_entry.py b/addons/account/tests/test_account_move_entry.py index 0b9c45f7073..a3d5bcedb01 100644 --- a/addons/account/tests/test_account_move_entry.py +++ b/addons/account/tests/test_account_move_entry.py @@ -898,12 +898,11 @@ class TestAccountMove(AccountTestInvoicingCommon): ]) moves.action_post() - res = moves.line_ids\ + moves.line_ids\ .filtered(lambda x: x.account_id == self.company_data['default_account_receivable'])\ .reconcile() - self.assertTrue(res.get('partials')) - exchange_diff = res['partials'].exchange_move_id + exchange_diff = moves.line_ids.matched_debit_ids.exchange_move_id self.assertTrue(exchange_diff) with self.assertRaises(UserError), self.cr.savepoint(): exchange_diff.button_draft() diff --git a/addons/account/tests/test_account_move_reconcile.py b/addons/account/tests/test_account_move_reconcile.py index 3f05e4e2344..eda6a267d07 100644 --- a/addons/account/tests/test_account_move_reconcile.py +++ b/addons/account/tests/test_account_move_reconcile.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- # pylint: disable=C0326 +from contextlib import contextmanager + from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.tests import tagged from odoo.tests.common import Form @@ -199,6 +201,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): # during the reconciliation. # ------------------------------------------------------------------------- + def _get_partials(self, amls): + return (amls.matched_debit_ids | amls.matched_credit_ids).sorted() + + def _get_caba_moves(self, moves): + return moves.search([('tax_cash_basis_origin_move_id', 'in', moves.ids)]) + def _create_line_for_reconciliation(self, balance, amount_currency, currency, move_date): move = self.env['account.move'].create({ 'move_type': 'entry', @@ -268,8 +276,8 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): ): with self.subTest(sub_test_name=sub_test_name): - res = batch.reconcile() - self.assertTrue(res.get('full_reconcile')) + batch.reconcile() + self.assertTrue(batch.full_reconcile_id) self.assertRecordValues( batch, [{'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}] * len(batch), @@ -307,12 +315,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line1 = self._create_line_for_reconciliation(*line1_vals) line2 = self._create_line_for_reconciliation(*line2_vals) with self.subTest(line1_vals=line1_vals, line2_vals=line2_vals): - - res = (line1 + line2).reconcile() + (line1 + line2).reconcile() # Reconcile. # Don't check the result since this is already checked by another tests. - exchange_diff = res['partials'].exchange_move_id + partials = self._get_partials(line1 + line2) + exchange_diff = partials.exchange_move_id self.assertTrue(exchange_diff) # Unreconcile. @@ -387,10 +395,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(0.0, -0.01, currency_1, '2017-01-01') line_2 = self._create_line_for_reconciliation(0.0, 0.02, currency_2, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertFalse(res['partials']) + self.assertFalse(partials) def test_reconcile_lines_corner_case_2_zero_amount_currency_same_foreign_currency(self): """ Test a corner case when both lines have something to reconcile in company currency but nothing @@ -404,20 +414,31 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-0.01, 0.0, currency, '2017-01-01') line_2 = self._create_line_for_reconciliation(0.02, 0.0, currency, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertRecordValues(res['partials'], [{ - 'amount': 0.01, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - 'exchange_move_id': None, - }]) - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['full_reconcile'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(partials, [ + { + 'amount': 0.01, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 0.01, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': full_reconcile.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(full_reconcile.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(full_reconcile.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 0.01, @@ -433,14 +454,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 0.01, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['full_reconcile'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -455,18 +469,38 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-0.01, -0.01, comp_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(0.0, 0.03, foreign_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertRecordValues(res['partials'], [{ - 'amount': 0.0, - 'debit_amount_currency': 0.02, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(partials, [ + { + 'amount': 0.0, + 'debit_amount_currency': 0.02, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 0.01, + 'debit_amount_currency': 0.01, + 'credit_amount_currency': 0.01, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 0.0, + 'debit_amount_currency': 0.01, + 'credit_amount_currency': 0.01, + 'debit_move_id': line_2.id, + 'credit_move_id': full_reconcile.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.01, 'credit': 0.0, @@ -482,9 +516,8 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['full_reconcile'].exchange_move_id.line_ids, [ + self.assertRecordValues(full_reconcile.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(full_reconcile.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 0.0, @@ -500,23 +533,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [ - { - 'amount': 0.01, - 'debit_amount_currency': 0.01, - 'credit_amount_currency': 0.01, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }, - { - 'amount': 0.0, - 'debit_amount_currency': 0.01, - 'credit_amount_currency': 0.01, - 'debit_move_id': line_2.id, - 'credit_move_id': res['full_reconcile'].exchange_move_id.line_ids[0].id, - }, - ]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -527,10 +544,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-0.01, 0.0, foreign_curr2, '2017-01-01') line_2 = self._create_line_for_reconciliation(0.01, 0.03, foreign_curr1, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ + self.assertRecordValues(partials, [{ 'amount': 0.01, 'debit_amount_currency': 0.03, 'credit_amount_currency': 0.0, @@ -538,28 +557,70 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'credit_move_id': line_1.id, 'exchange_move_id': None, }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) + def test_reconcile_lines_corner_case_5_zero_balance_in_one_line_same_foreign_currency(self): + """ Test a corner case when both lines have something to reconcile in company currency but one has nothing + in foreign currency. It could be due to: + - a bad usage of the `no_exchange_difference` context key + - a partial reconciliation made before migrating to this version + - some rounding error when dealing with currencies having != decimal places + - strange journal items made by the user + """ + currency = self.currency_data['currency'] + + line_1 = self._create_line_for_reconciliation(-0.06, 0.0, currency, '2017-01-01') + line_2 = self._create_line_for_reconciliation(0.12, 0.24, currency, '2017-01-01') + amls = line_1 + line_2 + + amls.reconcile() + partials = self._get_partials(amls) + + self.assertRecordValues(partials, [ + { + 'amount': 0.06, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + ]) + self.assertRecordValues(amls, [ + {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, + {'amount_residual': 0.06, 'amount_residual_currency': 0.24, 'reconciled': False}, + ]) + def test_reconcile_exchange_difference_on_partial_same_foreign_currency_debit_expense_partial_payment(self): currency = self.currency_data['currency'] line_1 = self._create_line_for_reconciliation(60.0, 120.0, currency, '2017-01-01') line_2 = self._create_line_for_reconciliation(-80.0, -240.0, currency, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_1.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -575,14 +636,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_1.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': -40.0, 'amount_residual_currency': -120.0, 'reconciled': False}, ]) @@ -592,18 +646,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(40.0, 120.0, currency, '2016-01-01') line_2 = self._create_line_for_reconciliation(-120.0, -240.0, currency, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_2.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -619,14 +684,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': -60.0, 'amount_residual_currency': -120.0, 'reconciled': False}, ]) @@ -636,18 +694,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-40.0, -120.0, currency, '2016-01-01') line_2 = self._create_line_for_reconciliation(120.0, 240.0, currency, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -663,14 +732,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 60.0, 'amount_residual_currency': 120.0, 'reconciled': False}, ]) @@ -680,18 +742,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-60.0, -120.0, currency, '2017-01-01') line_2 = self._create_line_for_reconciliation(80.0, 240.0, currency, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -707,14 +780,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 40.0, 'amount_residual_currency': 120.0, 'reconciled': False}, ]) @@ -725,18 +791,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(60.0, 120.0, foreign_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(-80.0, -80.0, comp_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_1.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -752,14 +829,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_1.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': -40.0, 'amount_residual_currency': -40.0, 'reconciled': False}, ]) @@ -770,18 +840,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(60.0, 60.0, comp_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(-80.0, -240.0, foreign_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': line_1.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -797,14 +878,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': line_1.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': -40.0, 'amount_residual_currency': -120.0, 'reconciled': False}, ]) @@ -815,18 +889,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(40.0, 120.0, foreign_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(-120.0, -120.0, comp_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_2.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -842,14 +927,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': -60.0, 'amount_residual_currency': -60.0, 'reconciled': False}, ]) @@ -860,18 +938,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(40.0, 40.0, comp_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(-120.0, -240.0, foreign_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_2.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -887,14 +976,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': -60.0, 'amount_residual_currency': -120.0, 'reconciled': False}, ]) @@ -905,18 +987,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-40.0, -120.0, foreign_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(120.0, 120.0, comp_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': line_2.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -932,14 +1025,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 60.0, 'amount_residual_currency': 60.0, 'reconciled': False}, ]) @@ -950,18 +1036,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-40.0, -40.0, comp_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(120.0, 240.0, foreign_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -977,14 +1074,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 60.0, 'amount_residual_currency': 120.0, 'reconciled': False}, ]) @@ -995,18 +1085,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-60.0, -60.0, comp_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(80.0, 240.0, foreign_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1022,14 +1123,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 40.0, 'amount_residual_currency': 120.0, 'reconciled': False}, ]) @@ -1040,18 +1134,29 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-60.0, -120.0, foreign_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(80.0, 80.0, comp_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1067,14 +1172,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 40.0, 'amount_residual_currency': 40.0, 'reconciled': False}, ]) @@ -1084,20 +1182,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(60.0, 120.0, currency, '2017-01-01') line_2 = self._create_line_for_reconciliation(-40.0, -120.0, currency, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_1.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -1113,14 +1223,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_1.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1130,20 +1233,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(40.0, 120.0, currency, '2016-01-01') line_2 = self._create_line_for_reconciliation(-60.0, -120.0, currency, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_2.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1159,14 +1274,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1176,20 +1284,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-40.0, -120.0, currency, '2016-01-01') line_2 = self._create_line_for_reconciliation(60.0, 120.0, currency, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + } + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -1205,14 +1325,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1222,20 +1335,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-60.0, -120.0, currency, '2017-01-01') line_2 = self._create_line_for_reconciliation(40.0, 120.0, currency, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + } + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1251,13 +1376,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }]) self.assertRecordValues(line_1 + line_2, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, @@ -1269,20 +1387,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(60.0, 120.0, foreign_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(-40.0, -40.0, comp_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_1.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -1298,14 +1428,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_1.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1316,20 +1439,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(60.0, 60.0, comp_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(-40.0, -120.0, foreign_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': line_1.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -1345,14 +1480,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': line_1.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1363,20 +1491,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(40.0, 120.0, foreign_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(-60.0, -60.0, comp_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_2.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1392,14 +1532,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1410,20 +1543,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(40.0, 40.0, comp_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(-60.0, -120.0, foreign_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_1.id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_1.id, + 'credit_move_id': line_2.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_2.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1439,14 +1584,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_2.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1457,20 +1595,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-40.0, -40.0, comp_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(60.0, 120.0, foreign_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': line_2.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -1486,14 +1636,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1504,20 +1647,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-40.0, -120.0, foreign_curr, '2016-01-01') line_2 = self._create_line_for_reconciliation(60.0, 60.0, comp_curr, '2017-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': line_2.id, + 'credit_move_id': partials.exchange_move_id.line_ids[0].id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.0, 'credit': 20.0, @@ -1533,14 +1688,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_expense_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': line_2.id, - 'credit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1551,20 +1699,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-60.0, -60.0, comp_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(40.0, 120.0, foreign_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 120.0, - 'credit_amount_currency': 40.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 120.0, + 'credit_amount_currency': 40.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 20.0, + 'credit_amount_currency': 20.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1580,14 +1740,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 20.0, - 'credit_amount_currency': 20.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1598,20 +1751,32 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): line_1 = self._create_line_for_reconciliation(-60.0, -120.0, foreign_curr, '2017-01-01') line_2 = self._create_line_for_reconciliation(40.0, 40.0, comp_curr, '2016-01-01') + amls = line_1 + line_2 - res = (line_1 + line_2).reconcile() + amls.reconcile() + partials = self._get_partials(amls) + full_reconcile = amls.full_reconcile_id - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': False}]) - self.assertRecordValues(res['partials'], [{ - 'amount': 40.0, - 'debit_amount_currency': 40.0, - 'credit_amount_currency': 120.0, - 'debit_move_id': line_2.id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertTrue(full_reconcile) + self.assertRecordValues(full_reconcile, [{'exchange_move_id': False}]) + self.assertRecordValues(partials, [ + { + 'amount': 40.0, + 'debit_amount_currency': 40.0, + 'credit_amount_currency': 120.0, + 'debit_move_id': line_2.id, + 'credit_move_id': line_1.id, + }, + { + 'amount': 20.0, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': line_1.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2017-01-01')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 20.0, 'credit': 0.0, @@ -1627,14 +1792,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 20.0, - 'debit_amount_currency': 0.0, - 'credit_amount_currency': 0.0, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': line_1.id, - }]) - self.assertRecordValues(line_1 + line_2, [ + self.assertRecordValues(amls, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) @@ -1794,9 +1952,10 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 839.40, 'amount_residual_currency': 839.40, 'reconciled': False}, ]) - res = (refund1_rec_line + inv1_rec_line).reconcile() + (refund1_rec_line + inv1_rec_line).reconcile() + partials = self._get_partials(refund1_rec_line + inv1_rec_line) - self.assertRecordValues(res['partials'], [{ + self.assertRecordValues(partials, [{ 'amount': 839.4, 'debit_amount_currency': 839.4, 'credit_amount_currency': 839.4, @@ -1821,17 +1980,28 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 36740.69, 'amount_residual_currency': 1935.72, 'reconciled': False}, ]) - res = (refund1_rec_line + inv2_rec_line).reconcile() + partials = self._get_partials(refund1_rec_line + inv2_rec_line) + (refund1_rec_line + inv2_rec_line).reconcile() + partials = self._get_partials(refund1_rec_line + inv2_rec_line) - partials - self.assertRecordValues(res['partials'], [{ - 'amount': 540.18, - 'debit_amount_currency': 28.46, - 'credit_amount_currency': 540.18, - 'debit_move_id': inv2_rec_line.id, - 'credit_move_id': refund1_rec_line.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2019-06-24')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 540.18, + 'debit_amount_currency': 28.46, + 'credit_amount_currency': 540.18, + 'debit_move_id': inv2_rec_line.id, + 'credit_move_id': refund1_rec_line.id, + }, + { + 'amount': 6.34, + 'debit_amount_currency': 6.34, + 'credit_amount_currency': 6.34, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': refund1_rec_line.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2019-06-24')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 6.34, 'credit': 0.0, @@ -1847,13 +2017,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 6.34, - 'debit_amount_currency': 6.34, - 'credit_amount_currency': 6.34, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': refund1_rec_line.id, - }]) self.assertRecordValues(refund1_rec_line + inv2_rec_line, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 36200.51, 'amount_residual_currency': 1907.26, 'reconciled': False}, @@ -1861,14 +2024,14 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): self.assert_invoice_outstanding_reconciled_widget(refund1, { inv1.id: 839.40, inv2.id: 540.18, - res['partials'].exchange_move_id.id: 6.34, + partials.exchange_move_id.id: 6.34, }) self.assert_invoice_outstanding_to_reconcile_widget(refund1, {}) # 3th reconciliation inv1 + pay1 self.assert_invoice_outstanding_reconciled_widget(inv2, { refund1.id: 28.46, - res['partials'].exchange_move_id.id: 6.34, + partials.exchange_move_id.id: 6.34, }) self.assert_invoice_outstanding_to_reconcile_widget(inv2, { pay1.move_id.id: 1907.17, @@ -1879,17 +2042,28 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': -36511.34, 'amount_residual_currency': -1907.17, 'reconciled': False}, ]) - res = (inv2_rec_line + pay1_rec_line).reconcile() + partials = self._get_partials(inv2_rec_line + pay1_rec_line) + (inv2_rec_line + pay1_rec_line).reconcile() + partials = self._get_partials(inv2_rec_line + pay1_rec_line) - partials - self.assertRecordValues(res['partials'], [{ - 'amount': 36198.80, - 'debit_amount_currency': 1907.17, - 'credit_amount_currency': 1907.17, - 'debit_move_id': inv2_rec_line.id, - 'credit_move_id': pay1_rec_line.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2019-06-28')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 36198.80, + 'debit_amount_currency': 1907.17, + 'credit_amount_currency': 1907.17, + 'debit_move_id': inv2_rec_line.id, + 'credit_move_id': pay1_rec_line.id, + }, + { + 'amount': 312.54, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': pay1_rec_line.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2019-06-28')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 312.54, 'credit': 0.0, @@ -1909,12 +2083,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 1.71, 'amount_residual_currency': 0.09, 'reconciled': False}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) - payment_exchange_id = inv2_rec_line.matched_credit_ids.filtered(lambda x: x.id != res['partials'].id) + payment_exchange_id = inv2_rec_line.matched_credit_ids.filtered(lambda x: x not in partials) self.assert_invoice_outstanding_reconciled_widget(inv2, { refund1.id: 28.46, pay1.move_id.id: 1907.17, - res['partials'].exchange_move_id.id: 312.54, + partials.exchange_move_id.id: 312.54, payment_exchange_id[0].exchange_move_id.id: 6.34, }) @@ -1927,17 +2101,28 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': -1.77, 'amount_residual_currency': -0.09, 'reconciled': False}, ]) - res = (inv2_rec_line + pay2_rec_line).reconcile() + partials = self._get_partials(inv2_rec_line + pay2_rec_line) + (inv2_rec_line + pay2_rec_line).reconcile() + partials = self._get_partials(inv2_rec_line + pay2_rec_line) - partials - self.assertRecordValues(res['partials'], [{ - 'amount': 1.71, - 'debit_amount_currency': 0.09, - 'credit_amount_currency': 0.09, - 'debit_move_id': inv2_rec_line.id, - 'credit_move_id': pay2_rec_line.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2019-09-24')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 1.71, + 'debit_amount_currency': 0.09, + 'credit_amount_currency': 0.09, + 'debit_move_id': inv2_rec_line.id, + 'credit_move_id': pay2_rec_line.id, + }, + { + 'amount': 0.06, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': pay2_rec_line.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2019-09-24')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.06, 'credit': 0.0, @@ -1957,20 +2142,19 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) - payment_exchange_id = inv2_rec_line.matched_credit_ids.filtered(lambda x: x.id != res['partials'].id) + payment_exchange_id = inv2_rec_line.matched_credit_ids.filtered(lambda x: x not in partials) self.assert_invoice_outstanding_reconciled_widget(inv2, { refund1.id: 28.46, pay1.move_id.id: 1907.17, pay2.move_id.id: 0.09, - res['partials'].exchange_move_id.id: 0.06, + partials.exchange_move_id.id: 0.06, payment_exchange_id[0].exchange_move_id.id: 6.34, payment_exchange_id[1].exchange_move_id.id: 312.54, }) self.assert_invoice_outstanding_to_reconcile_widget(inv2, {}) - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': None}]) + self.assertRecordValues(inv2_rec_line.full_reconcile_id, [{'exchange_move_id': None}]) def test_reconcile_special_mexican_workflow_2(self): comp_curr = self.company_data['currency'] @@ -2077,9 +2261,10 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 839.40, 'amount_residual_currency': 839.40, 'reconciled': False}, ]) - res = (refund1_rec_line + inv1_rec_line).reconcile() + (refund1_rec_line + inv1_rec_line).reconcile() + partials = self._get_partials(refund1_rec_line + inv1_rec_line) - self.assertRecordValues(res['partials'], [{ + self.assertRecordValues(partials, [{ 'amount': 839.4, 'debit_amount_currency': 839.4, 'credit_amount_currency': 839.4, @@ -2106,17 +2291,27 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 36740.69, 'amount_residual_currency': 1935.72, 'reconciled': False}, ]) - res = (inv2_rec_line + pay1_rec_line).reconcile() + (inv2_rec_line + pay1_rec_line).reconcile() + partials = self._get_partials(inv2_rec_line + pay1_rec_line) - self.assertRecordValues(res['partials'], [{ - 'amount': 36198.8, - 'debit_amount_currency': 1907.17, - 'credit_amount_currency': 1907.17, - 'debit_move_id': inv2_rec_line.id, - 'credit_move_id': pay1_rec_line.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2019-06-28')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 36198.8, + 'debit_amount_currency': 1907.17, + 'credit_amount_currency': 1907.17, + 'debit_move_id': inv2_rec_line.id, + 'credit_move_id': pay1_rec_line.id, + }, + { + 'amount': 312.54, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': pay1_rec_line.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2019-06-28')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 312.54, 'credit': 0.0, @@ -2138,7 +2333,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): ]) self.assert_invoice_outstanding_reconciled_widget(inv2, { pay1.move_id.id: 1907.17, - res['partials'].exchange_move_id.id: 312.54, + partials.exchange_move_id.id: 312.54, }) self.assert_invoice_outstanding_to_reconcile_widget(inv2, { refund1.id: 28.46, @@ -2154,17 +2349,28 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 541.89, 'amount_residual_currency': 28.55, 'reconciled': False}, ]) - res = (refund1_rec_line + inv2_rec_line).reconcile() + partials = self._get_partials(refund1_rec_line + inv2_rec_line) + (refund1_rec_line + inv2_rec_line).reconcile() + partials = self._get_partials(refund1_rec_line + inv2_rec_line) - partials - self.assertRecordValues(res['partials'], [{ - 'amount': 540.18, - 'debit_amount_currency': 28.46, - 'credit_amount_currency': 540.18, - 'debit_move_id': inv2_rec_line.id, - 'credit_move_id': refund1_rec_line.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2019-06-24')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + self.assertRecordValues(partials, [ + { + 'amount': 540.18, + 'debit_amount_currency': 28.46, + 'credit_amount_currency': 540.18, + 'debit_move_id': inv2_rec_line.id, + 'credit_move_id': refund1_rec_line.id, + }, + { + 'amount': 6.34, + 'debit_amount_currency': 6.34, + 'credit_amount_currency': 6.34, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': refund1_rec_line.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2019-06-24')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 6.34, 'credit': 0.0, @@ -2180,13 +2386,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): 'account_id': self.exch_income_account.id, }, ]) - self.assertRecordValues(res['exchange_partials'], [{ - 'amount': 6.34, - 'debit_amount_currency': 6.34, - 'credit_amount_currency': 6.34, - 'debit_move_id': res['partials'].exchange_move_id.line_ids[0].id, - 'credit_move_id': refund1_rec_line.id, - }]) self.assertRecordValues(refund1_rec_line + inv2_rec_line, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 1.71, 'amount_residual_currency': 0.09, 'reconciled': False}, @@ -2194,7 +2393,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): self.assert_invoice_outstanding_reconciled_widget(refund1, { inv1.id: 839.40, inv2.id: 540.18, - res['partials'].exchange_move_id.id: 6.34, + partials.exchange_move_id.id: 6.34, }) self.assert_invoice_outstanding_to_reconcile_widget(refund1, {}) @@ -2202,16 +2401,28 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): pay2.move_id.id: 0.09, }) - res = (inv2_rec_line + pay2_rec_line).reconcile() - self.assertRecordValues(res['partials'], [{ - 'amount': 1.71, - 'debit_amount_currency': 0.09, - 'credit_amount_currency': 0.09, - 'debit_move_id': inv2_rec_line.id, - 'credit_move_id': pay2_rec_line.id, - }]) - self.assertRecordValues(res['partials'].exchange_move_id, [{'date': fields.Date.from_string('2019-09-24')}]) - self.assertRecordValues(res['partials'].exchange_move_id.line_ids, [ + partials = self._get_partials(inv2_rec_line + pay2_rec_line) + (inv2_rec_line + pay2_rec_line).reconcile() + partials = self._get_partials(inv2_rec_line + pay2_rec_line) - partials + + self.assertRecordValues(partials, [ + { + 'amount': 1.71, + 'debit_amount_currency': 0.09, + 'credit_amount_currency': 0.09, + 'debit_move_id': inv2_rec_line.id, + 'credit_move_id': pay2_rec_line.id, + }, + { + 'amount': 0.06, + 'debit_amount_currency': 0.0, + 'credit_amount_currency': 0.0, + 'debit_move_id': partials.exchange_move_id.line_ids[0].id, + 'credit_move_id': pay2_rec_line.id, + }, + ]) + self.assertRecordValues(partials.exchange_move_id, [{'date': fields.Date.from_string('2019-09-24')}]) + self.assertRecordValues(partials.exchange_move_id.line_ids, [ { 'debit': 0.06, 'credit': 0.0, @@ -2231,20 +2442,19 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True}, ]) - payment_exchange_id = inv2_rec_line.matched_credit_ids.filtered(lambda x: x.id != res['partials'].id) + payment_exchange_id = inv2_rec_line.matched_credit_ids.filtered(lambda x: x not in partials) self.assert_invoice_outstanding_reconciled_widget(inv2, { refund1.id: 28.46, pay1.move_id.id: 1907.17, pay2.move_id.id: 0.09, - res['partials'].exchange_move_id.id: 0.06, + partials.exchange_move_id.id: 0.06, payment_exchange_id[0].exchange_move_id.id: 312.54, payment_exchange_id[1].exchange_move_id.id: 6.34, }) self.assert_invoice_outstanding_to_reconcile_widget(inv2, {}) - self.assertTrue(res.get('full_reconcile')) - self.assertRecordValues(res['full_reconcile'], [{'exchange_move_id': None}]) + self.assertRecordValues(inv2_rec_line.full_reconcile_id, [{'exchange_move_id': None}]) def test_migration_to_new_reconciliation_same_foreign_currency(self): foreign_curr = self.currency_data['currency'] @@ -2357,6 +2567,74 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): self.assertTrue(invoice.payment_state in ('in_payment', 'paid')) + def test_reconcile_plan(self): + @contextmanager + def rollback(): + savepoint = self.cr.savepoint() + yield + savepoint.rollback() + + comp_curr = self.company_data['currency'] + + line_1 = self._create_line_for_reconciliation(600.0, 600.0, comp_curr, '2017-01-01') + line_2 = self._create_line_for_reconciliation(-100.0, -100.0, comp_curr, '2017-01-02') + line_3 = self._create_line_for_reconciliation(700.0, 700.0, comp_curr, '2017-01-03') + line_5 = self._create_line_for_reconciliation(-700.0, -700.0, comp_curr, '2017-01-04') + line_4 = self._create_line_for_reconciliation(-500.0, -500.0, comp_curr, '2017-01-05') + + with rollback(): + # 5 batches of 1 aml. This won't reconcile anything. + self.env['account.move.line']._reconcile_plan([line_1, line_2, line_3, line_4, line_5]) + self.assertFalse(self._get_partials(line_1 + line_2 + line_3 + line_4 + line_5)) + + with rollback(): + # one batch of 5 amls. + self.env['account.move.line']._reconcile_plan([line_1 + line_2 + line_3 + line_4 + line_5]) + self.assertRecordValues( + self._get_partials(line_1 + line_2 + line_3 + line_4 + line_5), + [ + {'amount': 100.0, 'debit_move_id': line_1.id, 'credit_move_id': line_2.id}, + {'amount': 500.0, 'debit_move_id': line_1.id, 'credit_move_id': line_5.id}, + {'amount': 200.0, 'debit_move_id': line_3.id, 'credit_move_id': line_5.id}, + {'amount': 500.0, 'debit_move_id': line_3.id, 'credit_move_id': line_4.id}, + ], + ) + + with rollback(): + # Reconcile line_3 + line_5 and line_1 + line_4. line_2 is alone so will not be reconciled. + self.env['account.move.line']._reconcile_plan([line_3 + line_5, line_1 + line_4, line_2]) + self.assertRecordValues( + self._get_partials(line_1 + line_2 + line_3 + line_4 + line_5), + [ + {'amount': 700.0, 'debit_move_id': line_3.id, 'credit_move_id': line_5.id}, + {'amount': 500.0, 'debit_move_id': line_1.id, 'credit_move_id': line_4.id}, + ], + ) + + with rollback(): + # Reconcile line_3 + line_5 first, then line_1 + line_4, then the remaining amls with line_2. + self.env['account.move.line']._reconcile_plan([[line_3 + line_5, line_1 + line_4, line_2]]) + self.assertRecordValues( + self._get_partials(line_1 + line_2 + line_3 + line_4 + line_5), + [ + {'amount': 700.0, 'debit_move_id': line_3.id, 'credit_move_id': line_5.id}, + {'amount': 500.0, 'debit_move_id': line_1.id, 'credit_move_id': line_4.id}, + {'amount': 100.0, 'debit_move_id': line_1.id, 'credit_move_id': line_2.id}, + ], + ) + + with rollback(): + # Same as the previous test but with a lot of sub-plan to test the result is the same. + self.env['account.move.line']._reconcile_plan([[[line_3 + line_5], [[line_1 + line_4], line_2]]]) + self.assertRecordValues( + self._get_partials(line_1 + line_2 + line_3 + line_4 + line_5), + [ + {'amount': 700.0, 'debit_move_id': line_3.id, 'credit_move_id': line_5.id}, + {'amount': 500.0, 'debit_move_id': line_1.id, 'credit_move_id': line_4.id}, + {'amount': 100.0, 'debit_move_id': line_1.id, 'credit_move_id': line_2.id}, + ], + ) + # ------------------------------------------------------------------------- # Test creation of extra journal entries during the reconciliation to # deal with taxes that are exigible on payment (cash basis). @@ -2455,22 +2733,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines_1 = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_1.move_id) + receivable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_1.move_id) - tax_cash_basis_moves - self.assertFullReconcile(res['full_reconcile'], receivable_lines_1) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 2) - self.assertRecordValues(res['tax_cash_basis_moves'][0].line_ids, [ - # Base amount of tax_1 & tax_2: - {'debit': 25.0, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, - {'debit': 0.0, 'credit': 25.0, 'account_id': self.cash_basis_base_account.id}, - # tax_1: - {'debit': 8.33, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 8.33, 'account_id': self.tax_account_1.id}, - # tax_2: - {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, - ]) - self.assertRecordValues(res['tax_cash_basis_moves'][1].line_ids, [ + self.assertFullReconcile(receivable_lines_1.full_reconcile_id, receivable_lines_1) + self.assertEqual(len(tax_cash_basis_moves), 2) + self.assertRecordValues(tax_cash_basis_moves[0].line_ids, [ # Base amount of tax_1 & tax_2: {'debit': 8.33, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 8.33, 'account_id': self.cash_basis_base_account.id}, @@ -2481,6 +2750,17 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, ]) + self.assertRecordValues(tax_cash_basis_moves[1].line_ids, [ + # Base amount of tax_1 & tax_2: + {'debit': 25.0, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.0, 'credit': 25.0, 'account_id': self.cash_basis_base_account.id}, + # tax_1: + {'debit': 8.33, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 8.33, 'account_id': self.tax_account_1.id}, + # tax_2: + {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, + ]) self.assertAmountsGroupByAccount([ # Account Balance Amount Currency @@ -2496,22 +2776,24 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines_2 = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_2) - res = receivable_lines_2.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_2.move_id) + receivable_lines_2.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_2.move_id) - tax_cash_basis_moves - self.assertFullReconcile(res['full_reconcile'], receivable_lines_2) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 3) - self.assertRecordValues(res['tax_cash_basis_moves'][0].line_ids, [ + self.assertFullReconcile(receivable_lines_2.full_reconcile_id, receivable_lines_2) + self.assertEqual(len(tax_cash_basis_moves), 3) + self.assertRecordValues(tax_cash_basis_moves[0].line_ids, [ # Base amount of tax_1 & tax_2: - {'debit': 33.33, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, - {'debit': 0.0, 'credit': 33.33, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.01, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.0, 'credit': 0.01, 'account_id': self.cash_basis_base_account.id}, # tax_1: - {'debit': 11.11, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 11.11, 'account_id': self.tax_account_1.id}, + {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_1.id}, # tax_2: {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, ]) - self.assertRecordValues(res['tax_cash_basis_moves'][1].line_ids, [ + self.assertRecordValues(tax_cash_basis_moves[1].line_ids, [ # Base amount of tax_1 & tax_2: {'debit': 33.32, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 33.32, 'account_id': self.cash_basis_base_account.id}, @@ -2522,13 +2804,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, ]) - self.assertRecordValues(res['tax_cash_basis_moves'][2].line_ids, [ + self.assertRecordValues(tax_cash_basis_moves[2].line_ids, [ # Base amount of tax_1 & tax_2: - {'debit': 0.01, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, - {'debit': 0.0, 'credit': 0.01, 'account_id': self.cash_basis_base_account.id}, + {'debit': 33.33, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.0, 'credit': 33.33, 'account_id': self.cash_basis_base_account.id}, # tax_1: - {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_1.id}, + {'debit': 11.11, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 11.11, 'account_id': self.tax_account_1.id}, # tax_2: {'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, @@ -2546,11 +2828,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): payable_lines_1 = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_payable_account_1) - res = payable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(payable_lines_1.move_id) + payable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(payable_lines_1.move_id) - tax_cash_basis_moves - self.assertFullReconcile(res['full_reconcile'], payable_lines_1) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1) - self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [ + self.assertFullReconcile(payable_lines_1.full_reconcile_id, payable_lines_1) + self.assertEqual(len(tax_cash_basis_moves), 1) + self.assertRecordValues(tax_cash_basis_moves.line_ids, [ # Base amount of tax_1 & tax_2: {'debit': 0.01, 'credit': 0.0, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 0.01, 'account_id': self.cash_basis_base_account.id}, @@ -2562,7 +2846,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id}, ]) - self.assertRecordValues(res['full_reconcile'].exchange_move_id.line_ids, [ + self.assertRecordValues(payable_lines_1.full_reconcile_id.exchange_move_id.line_ids, [ {'account_id': self.tax_account_2.id, 'debit': 0.0, 'credit': 0.01, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_tiny_amount.id}, {'account_id': self.cash_basis_transfer_account.id, 'debit': 0.01, 'credit': 0.0, 'tax_ids': [], 'tax_line_id': False}, ]) @@ -2676,22 +2960,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines_1 = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines_1.reconcile() + receivable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_1.move_id) - self.assertFullReconcileAccount(res['full_reconcile'], self.extra_receivable_account_1) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 2) - self.assertRecordValues(res['tax_cash_basis_moves'][0].line_ids, [ - # Base amount of tax_1 & tax_2: - {'debit': 12.5, 'credit': 0.0, 'amount_currency': 25.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, - {'debit': 0.0, 'credit': 12.5, 'amount_currency': -25.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, - # tax_1: - {'debit': 4.17, 'credit': 0.0, 'amount_currency': 8.333, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 4.17, 'amount_currency': -8.333, 'currency_id': currency_id, 'account_id': self.tax_account_1.id}, - # tax_2: - {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.003, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.003, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, - ]) - self.assertRecordValues(res['tax_cash_basis_moves'][1].line_ids, [ + self.assertFullReconcileAccount(receivable_lines_1.full_reconcile_id, self.extra_receivable_account_1) + self.assertEqual(len(tax_cash_basis_moves), 2) + self.assertRecordValues(tax_cash_basis_moves[0].line_ids, [ # Base amount of tax_1 & tax_2: {'debit': 4.2, 'credit': 0.0, 'amount_currency': 8.331, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 4.2, 'amount_currency': -8.331, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, @@ -2702,8 +2976,19 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.001, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.001, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, ]) + self.assertRecordValues(tax_cash_basis_moves[1].line_ids, [ + # Base amount of tax_1 & tax_2: + {'debit': 12.5, 'credit': 0.0, 'amount_currency': 25.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.0, 'credit': 12.5, 'amount_currency': -25.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, + # tax_1: + {'debit': 4.17, 'credit': 0.0, 'amount_currency': 8.333, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 4.17, 'amount_currency': -8.333, 'currency_id': currency_id, 'account_id': self.tax_account_1.id}, + # tax_2: + {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.003, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.003, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, + ]) - caba_transition_lines_1 = res['tax_cash_basis_moves'].line_ids.filtered(lambda x: x.account_id == self.cash_basis_transfer_account) + caba_transition_lines_1 = tax_cash_basis_moves.line_ids.filtered(lambda x: x.account_id == self.cash_basis_transfer_account) caba_transition_exchange_moves_1 = caba_transition_lines_1.matched_credit_ids.exchange_move_id self.assertEqual(len(caba_transition_exchange_moves_1), 2) self.assertRecordValues(caba_transition_exchange_moves_1[0].line_ids, [ @@ -2729,33 +3014,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines_2 = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_2) - res = receivable_lines_2.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_2.move_id) + receivable_lines_2.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines_2.move_id) - tax_cash_basis_moves - self.assertFullReconcileAccount(res['full_reconcile'], self.extra_receivable_account_2) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 3) - self.assertRecordValues(res['tax_cash_basis_moves'][0].line_ids, [ - # Base amount of tax_1 & tax_2: - {'debit': 16.67, 'credit': 0.0, 'amount_currency': 33.331, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, - {'debit': 0.0, 'credit': 16.67, 'amount_currency': -33.331, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, - # tax_1: - {'debit': 5.56, 'credit': 0.0, 'amount_currency': 11.109, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 5.56, 'amount_currency': -11.109, 'currency_id': currency_id, 'account_id': self.tax_account_1.id}, - # tax_2: - {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.003, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.003, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, - ]) - self.assertRecordValues(res['tax_cash_basis_moves'][1].line_ids, [ - # Base amount of tax_1 & tax_2: - {'debit': 16.66, 'credit': 0.0, 'amount_currency': 33.323, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, - {'debit': 0.0, 'credit': 16.66, 'amount_currency': -33.323, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, - # tax_1: - {'debit': 5.55, 'credit': 0.0, 'amount_currency': 11.107, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 5.55, 'amount_currency': -11.107, 'currency_id': currency_id, 'account_id': self.tax_account_1.id}, - # tax_2: - {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.003, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.003, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, - ]) - self.assertRecordValues(res['tax_cash_basis_moves'][2].line_ids, [ + self.assertFullReconcileAccount(receivable_lines_2.full_reconcile_id, self.extra_receivable_account_2) + self.assertEqual(len(tax_cash_basis_moves), 3) + self.assertRecordValues(tax_cash_basis_moves[0].line_ids, [ # Base amount of tax_1 & tax_2: {'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.007, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 0.01, 'amount_currency': -0.007, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, @@ -2766,22 +3031,44 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, ]) + self.assertRecordValues(tax_cash_basis_moves[1].line_ids, [ + # Base amount of tax_1 & tax_2: + {'debit': 16.66, 'credit': 0.0, 'amount_currency': 33.323, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.0, 'credit': 16.66, 'amount_currency': -33.323, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, + # tax_1: + {'debit': 5.55, 'credit': 0.0, 'amount_currency': 11.107, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 5.55, 'amount_currency': -11.107, 'currency_id': currency_id, 'account_id': self.tax_account_1.id}, + # tax_2: + {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.003, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.003, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, + ]) + self.assertRecordValues(tax_cash_basis_moves[2].line_ids, [ + # Base amount of tax_1 & tax_2: + {'debit': 16.67, 'credit': 0.0, 'amount_currency': 33.331, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, + {'debit': 0.0, 'credit': 16.67, 'amount_currency': -33.331, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, + # tax_1: + {'debit': 5.56, 'credit': 0.0, 'amount_currency': 11.109, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 5.56, 'amount_currency': -11.109, 'currency_id': currency_id, 'account_id': self.tax_account_1.id}, + # tax_2: + {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.003, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.003, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, + ]) - caba_transition_lines_2 = res['tax_cash_basis_moves'].line_ids.filtered(lambda x: x.account_id == self.cash_basis_transfer_account) + caba_transition_lines_2 = tax_cash_basis_moves.line_ids.filtered(lambda x: x.account_id == self.cash_basis_transfer_account) caba_transition_exchange_moves_2 = caba_transition_lines_2.matched_credit_ids.exchange_move_id self.assertEqual(len(caba_transition_exchange_moves_2), 3) self.assertRecordValues(caba_transition_exchange_moves_2[0].line_ids, [ + {'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, + {'debit': 0.0, 'credit': 0.01, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.env.company.income_currency_exchange_account_id.id}, + ]) + self.assertRecordValues(caba_transition_exchange_moves_2[1].line_ids, [ {'debit': 0.0, 'credit': 1.86, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 1.86, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.env.company.expense_currency_exchange_account_id.id}, ]) - self.assertRecordValues(caba_transition_exchange_moves_2[1].line_ids, [ + self.assertRecordValues(caba_transition_exchange_moves_2[2].line_ids, [ {'debit': 0.0, 'credit': 1.85, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, {'debit': 1.85, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.env.company.expense_currency_exchange_account_id.id}, ]) - self.assertRecordValues(caba_transition_exchange_moves_2[2].line_ids, [ - {'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id}, - {'debit': 0.0, 'credit': 0.01, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.env.company.income_currency_exchange_account_id.id}, - ]) self.assertAmountsGroupByAccount([ # Account Balance Amount Currency @@ -2795,11 +3082,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): payable_lines_1 = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_payable_account_1) - res = payable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(payable_lines_1.move_id) + payable_lines_1.reconcile() + tax_cash_basis_moves = self._get_caba_moves(payable_lines_1.move_id) - tax_cash_basis_moves - self.assertFullReconcile(res['full_reconcile'], payable_lines_1) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1) - self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [ + self.assertFullReconcile(payable_lines_1.full_reconcile_id, payable_lines_1) + self.assertEqual(len(tax_cash_basis_moves), 1) + self.assertRecordValues(tax_cash_basis_moves.line_ids, [ # Base amount of tax_1 & tax_2: {'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.007, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 0.01, 'amount_currency': -0.007, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, @@ -2811,13 +3100,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.tax_account_2.id}, ]) - self.assertRecordValues(res['full_reconcile'].exchange_move_id.line_ids, [ + self.assertRecordValues(payable_lines_1.full_reconcile_id.exchange_move_id.line_ids, [ {'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.001, 'tax_ids': taxes.ids, 'tax_line_id': False}, {'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.001, 'tax_ids': [], 'tax_line_id': False}, ]) # No exchange move should have been created when reconciling the transition account - caba_transition_lines_3 = res['tax_cash_basis_moves'].line_ids.filtered(lambda x: x.account_id == self.cash_basis_transfer_account) + caba_transition_lines_3 = tax_cash_basis_moves.line_ids.filtered(lambda x: x.account_id == self.cash_basis_transfer_account) self.assertFalse(caba_transition_lines_3.matched_credit_ids.exchange_move_id) self.assertAmountsGroupByAccount([ @@ -2920,10 +3209,11 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines.reconcile() + receivable_lines.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines.move_id) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1) - self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [ + self.assertEqual(len(tax_cash_basis_moves), 1) + self.assertRecordValues(tax_cash_basis_moves.line_ids, [ # Base amount: {'debit': 150.0, 'credit': 0.0, 'amount_currency': 300.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 150.0, 'amount_currency': -300.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, @@ -2934,10 +3224,10 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines2 = (payment_move + end_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines2.reconcile() + receivable_lines2.reconcile() - self.assertTrue(res.get('full_reconcile')) - exchange_diff = res['full_reconcile'].exchange_move_id + self.assertTrue(receivable_lines2.full_reconcile_id) + exchange_diff = receivable_lines2.full_reconcile_id.exchange_move_id caba_rounding_correction = exchange_diff.line_ids\ .filtered(lambda line: line.account_id == self.cash_basis_transfer_account)\ .sorted(lambda line: (line.account_id, line.debit, line.credit)) @@ -3203,15 +3493,16 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines = (cash_basis_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines.reconcile() + receivable_lines.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines.move_id) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1) + self.assertEqual(len(tax_cash_basis_moves), 1) # Tax values based on payment # Invoice amount 300 (amount currency) with payment rate 2 (400 payment amount divided by 200 invoice balance) # - Base amount: 150 company currency # - Tax amount: 50 company currency - self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [ + self.assertRecordValues(tax_cash_basis_moves.line_ids, [ # Base amount: {'debit': 150.0, 'credit': 0.0, 'amount_currency': 300.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 150.0, 'amount_currency': -300.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id}, @@ -3227,7 +3518,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): ]) # Exchange difference - self.assertFalse(res['full_reconcile'].exchange_move_id, "No exchange move difference should be created for the full reconcile object ,as there is no cash basis rounding.") + self.assertFalse(receivable_lines.full_reconcile_id.exchange_move_id, "No exchange move difference should be created for the full reconcile object ,as there is no cash basis rounding.") def test_reconcile_cash_basis_refund_multicurrency(self): self.env.company.tax_exigibility = True @@ -3268,7 +3559,9 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): invoice.action_post() refund.action_post() - (refund + invoice).line_ids.filtered(lambda x: x.account_id.account_type == 'asset_receivable').reconcile() + (refund + invoice).line_ids\ + .filtered(lambda x: x.account_id.account_type == 'asset_receivable')\ + .reconcile() # Check the cash basis moves self.assertRecordValues( @@ -3430,17 +3723,18 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines = (invoice_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines.reconcile() + receivable_lines.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines.move_id) # == Check reconciliation of invoice with payment == - self.assertFullReconcile(res['full_reconcile'], receivable_lines) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1) + self.assertFullReconcile(receivable_lines.full_reconcile_id, receivable_lines) + self.assertEqual(len(tax_cash_basis_moves), 1) # == Check the reconciliation of invoice with tax cash basis journal entry. # /!\ We make the assumption the tax cash basis journal entry is well created. - tax_cash_basis_move = res['tax_cash_basis_moves'] + tax_cash_basis_move = tax_cash_basis_moves taxes_lines = (invoice_move.line_ids + tax_cash_basis_move.line_ids.filtered('debit'))\ .filtered(lambda line: line.account_id == self.cash_basis_transfer_account) @@ -3453,7 +3747,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): tax_cash_basis_move_reverse = tax_cash_basis_move._reverse_moves(cancel=True) - self.assertFullReconcile(res['full_reconcile'], receivable_lines) + self.assertFullReconcile(receivable_lines.full_reconcile_id, receivable_lines) # == Check the reconciliation of the tax cash basis journal entry with its reverse == @@ -3515,12 +3809,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines = (invoice_move + refund_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines.reconcile() + receivable_lines.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines.move_id) - self.assertFullReconcile(res['full_reconcile'], receivable_lines) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 2) + self.assertFullReconcile(receivable_lines.full_reconcile_id, receivable_lines) + self.assertEqual(len(tax_cash_basis_moves), 2) - tax_cash_basis_moves = res['tax_cash_basis_moves'].sorted(lambda move: move.tax_cash_basis_origin_move_id.id) + tax_cash_basis_moves = tax_cash_basis_moves.sorted(lambda move: move.tax_cash_basis_origin_move_id.id) # Invoice: cb_lines = tax_cash_basis_moves[0].line_ids.sorted(lambda line: (-abs(line.balance), -line.debit, line.account_id)) @@ -3650,12 +3945,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): receivable_lines = (invoice_move + payment_move).line_ids\ .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines.reconcile() + receivable_lines.reconcile() + tax_cash_basis_moves = self._get_caba_moves(receivable_lines.move_id) - self.assertFullReconcile(res['full_reconcile'], receivable_lines) - self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1) + self.assertFullReconcile(receivable_lines.full_reconcile_id, receivable_lines) + self.assertEqual(len(tax_cash_basis_moves), 1) - self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [ + self.assertRecordValues(tax_cash_basis_moves.line_ids, [ # Base amount x 2 because there is two taxes: {'debit': 100.0, 'credit': 0.0, 'tax_ids': [], 'tax_tag_ids': [], 'account_id': self.cash_basis_base_account.id}, {'debit': 0.0, 'credit': 100.0, 'tax_ids': base_taxes.ids, 'tax_tag_ids': base_tags.ids, 'account_id': self.cash_basis_base_account.id}, diff --git a/addons/account_edi/models/account_move.py b/addons/account_edi/models/account_move.py index 8d462578b5a..6996f1d15ef 100644 --- a/addons/account_edi/models/account_move.py +++ b/addons/account_edi/models/account_move.py @@ -471,16 +471,26 @@ class AccountMoveLine(models.Model): } return res - def reconcile(self): - # OVERRIDE + def _reconcile_pre_hook(self): + # EXTENDS 'account' # In some countries, the payments must be sent to the government under some condition. One of them could be # there is at least one reconciled invoice to the payment. Then, we need to update the state of the edi # documents during the reconciliation. + results = super()._reconcile_pre_hook() all_lines = self + self.matched_debit_ids.debit_move_id + self.matched_credit_ids.credit_move_id - payments = all_lines.move_id.filtered(lambda move: move.payment_id or move.statement_line_id) + results['edi_payments'] = all_lines.move_id\ + .filtered(lambda move: move.payment_id or move.statement_line_id) + results['edi_invoices_per_payment_before'] = { + pay: pay._get_reconciled_invoices() + for pay in results['edi_payments'] + } + return results - invoices_per_payment_before = {pay: pay._get_reconciled_invoices() for pay in payments} - res = super().reconcile() + def _reconcile_post_hook(self, data): + # EXTENDS 'account' + super()._reconcile_post_hook(data) + payments = data['edi_payments'] + invoices_per_payment_before = data['edi_invoices_per_payment_before'] invoices_per_payment_after = {pay: pay._get_reconciled_invoices() for pay in payments} changed_payments = self.env['account.move'] @@ -491,8 +501,6 @@ class AccountMoveLine(models.Model): changed_payments |= payment changed_payments._update_payments_edi_documents() - return res - def remove_move_reconcile(self): # OVERRIDE # When a payment has been sent to the government, it usually contains some information about reconciled diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py index d6d10d7efef..287155e8535 100644 --- a/addons/purchase_stock/tests/test_stockvaluation.py +++ b/addons/purchase_stock/tests/test_stockvaluation.py @@ -1104,11 +1104,11 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon): {'balance': 15.0, 'amount_currency': 30.0, 'account_id': self.stock_input_account.id}, {'balance': -15.0, 'amount_currency': -30.0, 'account_id': self.company_data['default_account_payable'].id}, ]) - self.assertRecordValues(inv.line_ids.full_reconcile_id.reconciled_line_ids, [ + self.assertRecordValues(inv.line_ids.full_reconcile_id.reconciled_line_ids.sorted('balance'), [ # pylint: disable=C0326 {'balance': -42.86, 'amount_currency': -30.0, 'account_id': self.stock_input_account.id}, - {'balance': 27.86, 'amount_currency': 0.0, 'account_id': self.stock_input_account.id}, {'balance': 15.0, 'amount_currency': 30.0, 'account_id': self.stock_input_account.id}, + {'balance': 27.86, 'amount_currency': 0.0, 'account_id': self.stock_input_account.id}, ]) def test_average_realtime_with_two_delivery_anglo_saxon_valuation_multicurrency_different_dates(self):