Skip to content
Permalink
Browse files

[MERGE] forward port branch saas-12.3 up to 9bf0b04

  • Loading branch information...
KangOl committed Sep 9, 2019
2 parents 71cc792 + 9bf0b04 commit 84a23408df1b50f79b4dfda8bd53bc2d7bc03f73
Showing with 2,263 additions and 1,719 deletions.
  1. +38 −23 addons/account/models/account.py
  2. +7 −1 addons/account/models/account_move.py
  3. +2 −1 addons/account/models/account_payment.py
  4. +4 −2 addons/account/models/account_reconcile_model.py
  5. +12 −6 addons/account/models/reconciliation_widget.py
  6. +80 −16 addons/account/tests/test_reconciliation.py
  7. +77 −0 addons/account/tests/test_reconciliation_widget.py
  8. +255 −7 addons/account/tests/test_tax.py
  9. +1 −1 addons/account/views/report_invoice.xml
  10. +3 −0 addons/account_cancel/views/account_views.xml
  11. +5 −0 addons/account_check_printing/models/account_payment.py
  12. +1 −0 addons/account_check_printing/views/account_payment_views.xml
  13. +4 −0 addons/account_tax_python/models/account_tax.py
  14. +4 −13 addons/auth_oauth/controllers/main.py
  15. +1 −0 addons/base_address_city/models/res_partner.py
  16. +5 −1 addons/base_import/models/base_import.py
  17. +6 −0 addons/bus/static/src/js/crosstab_bus.js
  18. +4 −2 addons/calendar/models/calendar.py
  19. +58 −0 addons/calendar/tests/test_calendar.py
  20. +3 −2 addons/crm_iap_lead_website/models/crm_reveal_rule.py
  21. +1 −0 addons/delivery/models/sale_order.py
  22. +0 −1 addons/delivery/wizard/choose_delivery_carrier.py
  23. +3 −3 addons/gamification/models/challenge.py
  24. +1 −1 addons/gamification/models/goal.py
  25. +3 −3 addons/gamification/views/challenge.xml
  26. +5 −2 addons/gamification/views/goal.xml
  27. +9 −4 addons/hr_expense/models/hr_expense.py
  28. +64 −0 addons/hr_expense/tests/test_expenses.py
  29. +1 −1 addons/hr_expense/wizard/hr_expense_sheet_register_payment.py
  30. +13 −3 addons/hr_holidays/models/hr_leave.py
  31. +1 −0 addons/hr_holidays/tests/__init__.py
  32. +212 −0 addons/hr_holidays/tests/test_automatic_leave_dates.py
  33. +1 −0 addons/hr_recruitment/views/hr_recruitment_views.xml
  34. +5 −2 addons/im_livechat/controllers/main.py
  35. +8 −0 addons/im_livechat/static/src/js/im_livechat.js
  36. +20 −42 addons/l10n_ae/data/account_tax_template_data.xml
  37. +14 −0 addons/l10n_ch/models/account_invoice.py
  38. +1 −0 addons/l10n_ch/models/account_journal.py
  39. +0 −14 addons/l10n_es/data/account_tax_data.xml
  40. +6 −6 addons/l10n_it_edi/models/ir_mail_server.py
  41. +7 −0 addons/l10n_lu/__init__.py
  42. +1 −1 addons/l10n_lu/__manifest__.py
  43. +13 −0 addons/l10n_multilang/models/l10n_multilang.py
  44. +1 −1 addons/l10n_uk/__manifest__.py
  45. +5 −3 addons/lunch/static/src/js/lunch_kanban_controller.js
  46. +47 −10 addons/lunch/static/src/js/lunch_kanban_model.js
  47. +20 −0 addons/lunch/static/src/js/lunch_kanban_view.js
  48. +50 −0 addons/lunch/static/tests/lunch_kanban_tests.js
  49. +1 −1 addons/mail/controllers/main.py
  50. +24 −3 addons/mail/static/src/js/models/threads/document_thread.js
  51. +5 −1 addons/mail/static/src/js/models/threads/searchable_thread.js
  52. +51 −22 addons/mail/static/src/js/services/mail_document_thread_manager.js
  53. +5 −1 addons/mail/static/src/js/services/mail_window_manager.js
  54. +18 −26 addons/mail/static/src/js/thread_windows/thread_window.js
  55. +6 −1 addons/mail/static/src/js/utils.js
  56. +7 −0 addons/mail/static/src/scss/composer.scss
  57. +1 −0 addons/mail/static/src/scss/thread.scss
  58. +54 −13 addons/mail/static/tests/discuss_mobile_tests.js
  59. +13 −7 addons/mail/static/tests/document_thread_window_tests.js
  60. +82 −0 addons/mail/static/tests/systray/systray_messaging_menu_tests.js
  61. +4 −4 addons/maintenance/views/maintenance_views.xml
  62. +3 −0 addons/mrp/models/mrp_production.py
  63. +0 −1 addons/mrp/models/res_config_settings.py
  64. +6 −1 addons/mrp/models/stock_move.py
  65. +0 −11 addons/mrp/views/res_config_settings_views.xml
  66. +0 −25 addons/mrp_subcontracting/__init__.py
  67. +0 −19 addons/mrp_subcontracting/__manifest__.py
  68. +0 −12 addons/mrp_subcontracting/data/mrp_subcontracting_data.xml
  69. +0 −9 addons/mrp_subcontracting/models/__init__.py
  70. +0 −20 addons/mrp_subcontracting/models/mrp_bom.py
  71. +0 −47 addons/mrp_subcontracting/models/res_company.py
  72. +0 −56 addons/mrp_subcontracting/models/res_partner.py
  73. +0 −41 addons/mrp_subcontracting/models/stock_move.py
  74. +0 −133 addons/mrp_subcontracting/models/stock_picking.py
  75. +0 −139 addons/mrp_subcontracting/models/stock_warehouse.py
  76. +0 −4 addons/mrp_subcontracting/tests/__init__.py
  77. +0 −526 addons/mrp_subcontracting/tests/test_subcontracting.py
  78. +0 −14 addons/mrp_subcontracting/views/mrp_bom_views.xml
  79. +0 −23 addons/mrp_subcontracting/views/stock_picking_views.xml
  80. +0 −13 addons/mrp_subcontracting/views/stock_warehouse_views.xml
  81. +1 −1 addons/payment/views/payment_portal_templates.xml
  82. +3 −3 addons/payment_sips/models/payment.py
  83. +3 −3 addons/payment_sips/views/payment_views.xml
  84. +9 −0 addons/point_of_sale/models/account_tax.py
  85. +17 −4 addons/point_of_sale/static/src/css/customer_facing_display.css
  86. +17 −2 addons/point_of_sale/static/src/js/models.js
  87. +22 −14 addons/point_of_sale/static/src/scss/customer_facing_display.scss
  88. +3 −2 addons/product/models/product_pricelist.py
  89. +8 −1 addons/product/models/product_template.py
  90. +22 −0 addons/product/tests/test_variants.py
  91. +0 −2 addons/purchase/models/purchase.py
  92. +0 −4 addons/purchase_mrp_subcontracting/__init__.py
  93. +0 −18 addons/purchase_mrp_subcontracting/__manifest__.py
  94. +0 −5 addons/purchase_mrp_subcontracting/models/__init__.py
  95. +0 −16 addons/purchase_mrp_subcontracting/models/product.py
  96. +0 −16 addons/purchase_mrp_subcontracting/models/stock_picking.py
  97. +0 −7 addons/purchase_mrp_subcontracting/tests/__init__.py
  98. +0 −112 addons/purchase_mrp_subcontracting/tests/test_purchase_subcontracting.py
  99. +0 −25 addons/purchase_mrp_subcontracting/views/supplier_info_views.xml
  100. +4 −2 addons/sale_coupon/models/sale_order.py
  101. +2 −2 addons/sale_coupon/views/sale_coupon_program_views.xml
  102. +2 −2 addons/sale_purchase/models/sale_order.py
  103. +1 −1 addons/sale_stock/models/stock.py
  104. +1 −1 addons/stock/models/stock_move.py
  105. +1 −1 addons/stock/models/stock_picking.py
  106. +2 −2 addons/stock/models/stock_quant.py
  107. +4 −2 addons/stock/models/stock_warehouse.py
  108. +47 −0 addons/stock/tests/test_packing.py
  109. +2 −2 addons/stock_landed_costs/models/stock_landed_cost.py
  110. +1 −0 addons/web/models/ir_qweb.py
  111. +13 −0 addons/web/static/src/js/core/abstract_storage_service.js
  112. +4 −6 addons/web/static/src/js/core/local_storage.js
  113. +14 −0 addons/web/static/src/js/core/ram_storage.js
  114. +7 −5 addons/web/static/src/js/fields/relational_fields.js
  115. +5 −3 addons/web/static/src/js/views/basic/basic_model.js
  116. +3 −1 addons/web/static/src/js/views/form/form_controller.js
  117. +4 −0 addons/web/static/src/js/views/form/form_view.js
  118. +3 −0 addons/web/static/src/js/views/graph/graph_view.js
  119. +8 −0 addons/web/static/src/js/views/kanban/kanban_record.js
  120. +2 −0 addons/web/static/src/js/views/list/list_controller.js
  121. +11 −1 addons/web/static/src/js/widgets/auto_complete.js
  122. +4 −1 addons/web/static/src/js/widgets/date_picker.js
  123. +8 −0 addons/web/static/src/scss/ui.scss
  124. +34 −5 addons/web/static/tests/fields/basic_fields_tests.js
  125. +31 −8 addons/web/static/tests/fields/relational_fields_tests.js
  126. +16 −5 addons/web/static/tests/helpers/mock_server.js
  127. +1 −2 addons/web/static/tests/helpers/test_utils_create.js
  128. +73 −2 addons/web/static/tests/views/form_tests.js
  129. +21 −0 addons/web/static/tests/views/graph_tests.js
  130. +55 −0 addons/web/static/tests/views/kanban_tests.js
  131. +89 −0 addons/web/static/tests/views/list_tests.js
  132. +4 −2 addons/web/static/tests/views/view_dialogs_tests.js
  133. +6 −1 addons/web_editor/static/src/js/wysiwyg/plugin/text.js
  134. +1 −1 addons/web_editor/static/src/js/wysiwyg_snippets/snippets.options.js
  135. +13 −12 addons/website/models/ir_ui_view.py
  136. +2 −5 addons/website/models/website.py
  137. +2 −1 addons/website/static/src/js/content/menu.js
  138. +8 −2 addons/website/static/src/scss/website.scss
  139. +1 −1 addons/website/views/website_templates.xml
  140. +8 −3 addons/website_blog/controllers/main.py
  141. +13 −10 addons/website_blog/static/src/js/website_blog.js
  142. +5 −4 addons/website_blog/views/website_blog_templates.xml
  143. +4 −1 addons/website_event_sale/models/sale_order.py
  144. +1 −1 addons/website_event_track/models/event_track.py
  145. +1 −1 addons/website_sale/models/website.py
  146. +1 −1 addons/website_sale/views/templates.xml
  147. +1 −1 addons/website_sale_comparison/models/website_sale_comparison.py
  148. +22 −0 addons/website_sale_coupon/views/website_sale_templates.xml
  149. +1 −1 addons/website_sale_delivery/static/src/js/website_sale_delivery.js
  150. +17 −0 addons/website_sale_delivery/static/tests/tours/website_free_delivery.js
  151. +4 −0 addons/website_sale_delivery/tests/test_ui.py
  152. +9 −2 addons/website_sale_digital/controllers/main.py
  153. +115 −0 addons/website_sale_slides/controllers/i18n/so.po
  154. +2 −2 addons/website_sale_stock/models/product_template.py
  155. +15 −14 addons/website_slides/static/src/js/slides_course_fullscreen_player.js
  156. +15 −0 doc/cla/corporate/arxilead.md
  157. +5 −2 doc/cla/corporate/tvtmarine-automation.md
  158. +11 −0 doc/cla/individual/hari4274.md
  159. +6 −0 doc/reference/cmdline.rst
  160. +2 −2 doc/reference/javascript_reference.rst
  161. +6 −0 doc/reference/orm.rst
  162. +1 −1 odoo/addons/base/data/ir_module_module.xml
  163. +1 −1 odoo/addons/base/models/ir_actions_report.py
  164. +1 −1 odoo/addons/base/models/ir_model.py
  165. +1 −6 odoo/addons/base/models/res_bank.py
  166. +6 −2 odoo/addons/base/models/res_partner.py
  167. +8 −0 odoo/addons/base/tests/test_base.py
  168. +3 −3 odoo/addons/base/views/ir_qweb_widget_templates.xml
  169. +1 −1 odoo/tools/mail.py
@@ -954,7 +954,7 @@ def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_ui
@api.depends('company_id')
def _belong_to_company(self):
for journal in self:
journal.belong_to_company = (journal.company_id.id == self.env.company.id)
journal.belongs_to_company = (journal.company_id.id == self.env.company.id)

@api.multi
def _search_company_journals(self, operator, value):
@@ -1361,20 +1361,27 @@ def recompute_base(base_amount, fixed_amount, percent_amount, division_amount):
cached_tax_amounts = {}
if handle_price_include:
for tax in reversed(taxes):
tax_repartition_lines = (
is_refund
and tax.refund_repartition_line_ids
or tax.invoice_repartition_line_ids
).filtered(lambda x: x.repartition_type == "tax")
sum_repartition_factor = sum(tax_repartition_lines.mapped("factor"))

if tax.include_base_amount:
base = recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount)
incl_fixed_amount = incl_percent_amount = incl_division_amount = 0
store_included_tax_total = True
if tax.price_include or self._context.get('force_price_include'):
if tax.amount_type == 'percent':
incl_percent_amount += tax.amount
incl_percent_amount += tax.amount * sum_repartition_factor
elif tax.amount_type == 'division':
incl_division_amount += tax.amount
incl_division_amount += tax.amount * sum_repartition_factor
elif tax.amount_type == 'fixed':
incl_fixed_amount += quantity * tax.amount
incl_fixed_amount += quantity * tax.amount * sum_repartition_factor
else:
# tax.amount_type == other (python)
tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner)
tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) * sum_repartition_factor
incl_fixed_amount += tax_amount
# Avoid unecessary re-computation
cached_tax_amounts[i] = tax_amount
@@ -1393,6 +1400,9 @@ def recompute_base(base_amount, fixed_amount, percent_amount, division_amount):
i = 0
cumulated_tax_included_amount = 0
for tax in taxes:
tax_repartition_lines = (is_refund and tax.refund_repartition_line_ids or tax.invoice_repartition_line_ids).filtered(lambda x: x.repartition_type == 'tax')
sum_repartition_factor = sum(tax_repartition_lines.mapped('factor'))

#compute the tax_amount
if (self._context.get('force_price_include') or tax.price_include) and total_included_checkpoints.get(i):
# We know the total to reach for that tax, so we make a substraction to avoid any rounding issues
@@ -1402,11 +1412,12 @@ def recompute_base(base_amount, fixed_amount, percent_amount, division_amount):
tax_amount = tax.with_context(force_price_include=False)._compute_amount(
base, sign * price_unit, quantity, product, partner)

# Round the tax_amount
# Round the tax_amount multiplied by the computed repartition lines factor.
tax_amount = round(tax_amount, prec)
factorized_tax_amount = round(tax_amount * sum_repartition_factor, prec)

if tax.price_include and not total_included_checkpoints.get(i):
cumulated_tax_included_amount += tax_amount
cumulated_tax_included_amount += factorized_tax_amount

# If the tax affects the base of subsequent taxes, its tax move lines must
# receive the base tags and tag_ids of these taxes, so that the tax report computes
@@ -1417,18 +1428,24 @@ def recompute_base(base_amount, fixed_amount, percent_amount, division_amount):
subsequent_taxes = taxes[i+1:]
subsequent_tags = subsequent_taxes.get_tax_tags(is_refund, 'base')

# Compute the tax lines
tax_repartition_lines = (is_refund and tax.refund_repartition_line_ids or tax.invoice_repartition_line_ids).filtered(lambda x: x.repartition_type == 'tax')
sum_factor = sum(tax_repartition_lines.mapped('factor'))
repartition_lines_to_treat = len(tax_repartition_lines)
total_amount = 0
for repartition_line in tax_repartition_lines:
if repartition_lines_to_treat != 1 or sum_factor != 1.0:
line_amount = round(tax_amount * repartition_line.factor, prec)
else:
# When the sum of the repartition lines factor is 100%, we need to ensure the whole tax amount has
# been spread into the repartition lines.
line_amount = round(tax_amount - total_amount, prec)
# Compute the tax line amounts by multiplying each factor with the tax amount.
# Then, spread the tax rounding to ensure the consistency of each line independently with the factorized
# amount. E.g:
#
# Suppose a tax having 4 x 50% repartition line applied on a tax amount of 0.03 with 2 decimal places.
# The factorized_tax_amount will be 0.06 (200% x 0.03). However, each line taken independently will compute
# 50% * 0.03 = 0.01 with rounding. It means there is 0.06 - 0.04 = 0.02 as total_rounding_error to dispatch
# in lines as 2 x 0.01.
repartition_line_amounts = [round(tax_amount * line.factor, prec) for line in tax_repartition_lines]
total_rounding_error = round(factorized_tax_amount - sum(repartition_line_amounts), prec)
nber_rounding_steps = int(abs(total_rounding_error / currency.rounding))
rounding_error = round(nber_rounding_steps and total_rounding_error / nber_rounding_steps or 0.0, prec)

for repartition_line, line_amount in zip(tax_repartition_lines, repartition_line_amounts):

if nber_rounding_steps:
line_amount += rounding_error
nber_rounding_steps -= 1

taxes_vals.append({
'id': tax.id,
@@ -1445,16 +1462,14 @@ def recompute_base(base_amount, fixed_amount, percent_amount, division_amount):
'tax_ids': subsequent_taxes.ids,
})

total_amount += line_amount
if not repartition_line.account_id:
total_void += line_amount
repartition_lines_to_treat -= 1

# Affect subsequent taxes
if tax.include_base_amount:
base += tax_amount
base += factorized_tax_amount

total_included += tax_amount
total_included += factorized_tax_amount
i += 1

return {
@@ -1964,6 +1964,7 @@ def button_draft(self):

@api.multi
def button_cancel(self):
self._check_tax_lock_date()
AccountMoveLine = self.env['account.move.line']
excluded_move_ids = []

@@ -2708,7 +2709,7 @@ def _amount_residual(self):
else:
date = partial_line.credit_move_id.date if partial_line.debit_move_id == line else partial_line.debit_move_id.date
rate = line.currency_id.with_context(date=date).rate
amount_residual_currency += sign_partial_line * partial_line.amount * rate
amount_residual_currency += sign_partial_line * line.currency_id.round(partial_line.amount * rate)

#computing the `reconciled` field.
reconciled = False
@@ -3003,6 +3004,11 @@ def default_get(self, default_fields):

# Suggest default value for debit / credit to balance the journal entry.
balance = sum(line['debit'] - line['credit'] for line in move.line_ids)
# if we are here, line_ids is in context, so journal_id should also be.
journal = self.env['account.journal'].browse(self._context.get('default_journal_id') or self._context['journal_id'])
currency = journal.exists() and journal.company_id.currency_id
if currency:
balance = currency.round(balance)
if balance < 0.0:
values.update({'debit': -balance})
if balance > 0.0:
@@ -452,7 +452,8 @@ def cancel(self):
for move in rec.move_line_ids.mapped('move_id'):
if rec.reconciled_invoice_ids:
move.line_ids.remove_move_reconcile()
move.button_cancel()
if move.state != 'draft':
move.button_cancel()
move.unlink()
rec.write({
'state': 'cancelled',
@@ -361,7 +361,7 @@ def _apply_conditions(self, query, params):
query += ' AND st_line.name NOT ILIKE %s'
params += ['%%%s%%' % rule.match_label_param]
elif rule.match_label == 'match_regex':
query += ' AND st_line.name ~ %s'
query += ' AND st_line.name ~* %s'
params += [rule.match_label_param]

# Filter on partners.
@@ -621,8 +621,10 @@ def _check_rule_propositions(self, statement_line, candidates):

if line_residual > total_residual:
amount_percentage = (total_residual / line_residual) * 100
else:
elif total_residual:
amount_percentage = (line_residual / total_residual) * 100 if total_residual else 0
else:
return False
return amount_percentage >= self.match_total_amount_param

@api.multi
@@ -116,7 +116,8 @@ def _get_bank_statement_line_partners(self, st_lines):
query += 'LEFT JOIN res_partner_bank bank ON bank.id = st_line.bank_account_id OR bank.acc_number = st_line.account_number %s\n' % (where_bank)
query += 'LEFT JOIN res_partner p1 ON st_line.partner_id=p1.id \n'
query += 'LEFT JOIN res_partner p2 ON bank.partner_id=p2.id \n'
query += 'LEFT JOIN res_partner p3 ON p3.name ILIKE st_line.partner_name %s\n' % (where_partner)
# By definition the commercial partner_id doesn't have a parent_id set
query += 'LEFT JOIN res_partner p3 ON p3.name ILIKE st_line.partner_name %s AND p3.parent_id is NULL \n' % (where_partner)
query += 'WHERE st_line.id IN %s'

params += [tuple(st_lines.ids)]
@@ -520,17 +521,22 @@ def _domain_move_lines_for_reconciliation(self, st_line, aml_accounts, partner_i
])

domain = expression.OR([domain_reconciliation, domain_matching])
partner_domain = []
if partner_id:
domain = expression.AND([domain, [('partner_id', '=', partner_id)]])
partner_domain = [('partner_id', '=', partner_id)]
domain = expression.AND([domain, partner_domain])

# Domain factorized for all reconciliation use cases
if search_str:
str_domain = self._domain_move_lines(search_str=search_str)
if not partner_id:
str_domain = expression.OR([
str_domain,
[('partner_id.name', 'ilike', search_str)]
])
partner_domain = [('partner_id.name', 'ilike', search_str)]

str_domain = expression.OR([
str_domain,
partner_domain,
])

domain = expression.AND([
domain,
str_domain
@@ -149,6 +149,24 @@ def create_invoice(self, type='out_invoice', invoice_amount=50, currency_id=None
invoice.post()
return invoice

def create_invoice_partner(self, type='out_invoice', invoice_amount=50, currency_id=None, partner_id=False, payment_term_id=False):
move = self.env['account.move'].with_context(default_type=type).create({
'type': type,
'partner_id': partner_id,
'invoice_date': time.strftime('%Y') + '-07-01',
'date': time.strftime('%Y') + '-07-01',
'currency_id': currency_id,
'invoice_payment_term_id': payment_term_id,
'invoice_line_ids': [(0, 0, {
'quantity': 1,
'price_unit': invoice_amount,

'name': 'product that cost ' + str(invoice_amount),
})],
})
move.post()
return move

def make_payment(self, invoice_record, bank_journal, amount=0.0, amount_currency=0.0, currency_id=None):
bank_stmt = self.acc_bank_stmt_model.create({
'journal_id': bank_journal.id,
@@ -895,22 +913,6 @@ def test_revert_payment_and_reconcile(self):
self.assertEqual(reversed_bank_line.full_reconcile_id.id, bank_line.full_reconcile_id.id)
self.assertEqual(reversed_customer_line.full_reconcile_id.id, customer_line.full_reconcile_id.id)

def create_invoice_partner(self, type='out_invoice', invoice_amount=50, currency_id=None, partner_id=False, payment_term_id=False):
move = self.env['account.move'].with_context(default_type=type).create({
'type': type,
'partner_id': partner_id,
'invoice_date': time.strftime('%Y') + '-07-01',
'date': time.strftime('%Y') + '-07-01',
'currency_id': currency_id,
'invoice_payment_term_id': payment_term_id,
'invoice_line_ids': [(0, 0, {
'quantity': 1,
'price_unit': invoice_amount,
'name': 'product that cost ' + str(invoice_amount),
})],
})
move.post()
return move

def test_aged_report(self):
AgedReport = self.env['report.account.report_agedpartnerbalance'].with_context(include_nullified_amount=True)
@@ -1168,6 +1170,68 @@ def test_partial_reconcile_currencies_02(self):
# because they owe us still 50 CC.
self.assertEqual(invoice_cust_1.invoice_payment_state, 'not_paid', 'Invoice is in status %s' % invoice_cust_1.state)

def test_inv_refund_foreign_payment_writeoff_domestic(self):
company = self.env.ref('base.main_company')
self.env['res.currency.rate'].search([]).unlink()
self.env['res.currency.rate'].create({
'name': time.strftime('%Y') + '-07-01',
'rate': 1.0,
'currency_id': self.currency_euro_id,
'company_id': company.id
})
self.env['res.currency.rate'].create({
'name': time.strftime('%Y') + '-07-01',
'rate': 1.113900, # Don't change this !
'currency_id': self.currency_usd_id,
'company_id': self.env.ref('base.main_company').id
})
inv1 = self.create_invoice(invoice_amount=480, currency_id=self.currency_usd_id)
inv2 = self.create_invoice(type="out_refund", invoice_amount=140, currency_id=self.currency_usd_id)

payment = self.env['account.payment'].create({
'payment_method_id': self.inbound_payment_method.id,
'payment_type': 'inbound',
'partner_type': 'customer',
'partner_id': inv1.partner_id.id,
'amount': 287.20,
'journal_id': self.bank_journal_euro.id,
'company_id': company.id,
})
payment.post()

inv1_receivable = inv1.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
inv2_receivable = inv2.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
pay_receivable = payment.move_line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')

data_for_reconciliation = [
{
'type': 'partner',
'id': inv1.partner_id.id,
'mv_line_ids': (inv1_receivable + inv2_receivable + pay_receivable).ids,
'new_mv_line_dicts': [
{
'credit': 18.04,
'debit': 0.00,
'journal_id': self.bank_journal_euro.id,
'name': 'Total WriteOff (Fees)',
'account_id': self.diff_expense_account.id
}
]
}
]

self.env["account.reconciliation.widget"].process_move_lines(data_for_reconciliation)

self.assertTrue(inv1_receivable.full_reconcile_id.exists())
self.assertEquals(inv1_receivable.full_reconcile_id, inv2_receivable.full_reconcile_id)
self.assertEquals(inv1_receivable.full_reconcile_id, pay_receivable.full_reconcile_id)

self.assertTrue(all(l.reconciled for l in inv1_receivable))
self.assertTrue(all(l.reconciled for l in inv2_receivable))

self.assertEquals(inv1.invoice_payment_state, 'paid')
self.assertEquals(inv2.invoice_payment_state, 'paid')

def test_multiple_term_reconciliation_opw_1906665(self):
'''Test that when registering a payment to an invoice with multiple
payment term lines the reconciliation happens against the line

0 comments on commit 84a2340

Please sign in to comment.
You can’t perform that action at this time.