Kenjx

Account Invoice Merge

Jun 10th, 2024
403
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.18 KB | None | 0 0
  1.  
  2. class AccountMove(models.Model):
  3.     _inherit = "account.move"
  4.  
  5.     @api.model
  6.     def _get_invoice_key_cols(self):
  7.         return [
  8.             "partner_id",
  9.             "user_id",
  10.             "move_type",
  11.             "currency_id",
  12.             "journal_id",
  13.             "company_id",
  14.             "partner_bank_id",
  15.         ]
  16.  
  17.     @api.model
  18.     def _get_invoice_line_key_cols(self):
  19.         fields = [
  20.             "name",
  21.             "discount",
  22.             "tax_ids",
  23.             "price_unit",
  24.             "product_id",
  25.             "account_id",
  26.             "analytic_account_id",
  27.             "product_uom_id",
  28.         ]
  29.         for field in [
  30.             "sale_line_ids",  # odoo/sale
  31.             "purchase_price",  # OCA/account_invoice_margin
  32.         ]:
  33.             if field in self.env["account.move.line"]._fields:
  34.                 fields.append(field)
  35.         return fields
  36.  
  37.     @api.model
  38.     def _get_first_invoice_fields(self, invoice):
  39.         return {
  40.             "invoice_origin": "%s" % (invoice.invoice_origin or "",),
  41.             "partner_id": invoice.partner_id.id,
  42.             "journal_id": invoice.journal_id.id,
  43.             "user_id": invoice.user_id.id,
  44.             "currency_id": invoice.currency_id.id,
  45.             "company_id": invoice.company_id.id,
  46.             "move_type": invoice.move_type,
  47.             "state": "draft",
  48.             "payment_reference": "%s" % (invoice.payment_reference or "",),
  49.             "name": "%s" % (invoice.name or "",),
  50.             "fiscal_position_id": invoice.fiscal_position_id.id,
  51.             "invoice_payment_term_id": invoice.invoice_payment_term_id.id,
  52.             "invoice_line_ids": {},
  53.             "partner_bank_id": invoice.partner_bank_id.id,
  54.         }
  55.  
  56.     def _get_draft_invoices(self):
  57.         """Overridable function to return draft invoices to merge"""
  58.         return self.filtered(lambda x: x.state == "draft")
  59.  
  60.     # flake8: noqa: C901
  61.     def do_merge(
  62.         self, keep_references=True, date_invoice=False, remove_empty_invoice_lines=True
  63.     ):
  64.         """
  65.        To merge similar type of account invoices.
  66.        Invoices will only be merged if:
  67.        * Account invoices are in draft
  68.        * Account invoices belong to the same partner
  69.        * Account invoices are have same company, partner, address, currency,
  70.          journal, currency, salesman, account, type
  71.        Lines will only be merged if:
  72.        * Invoice lines are exactly the same except for the quantity and unit
  73.  
  74.         @param self: The object pointer.
  75.         @param keep_references: If True, keep reference of original invoices
  76.  
  77.         @return: new account invoice id
  78.  
  79.        """
  80.  
  81.         def make_key(br, fields):
  82.             list_key = []
  83.             for field in fields:
  84.                 field_val = getattr(br, field)
  85.                 if field in ("product_id", "account_id"):
  86.                     if not field_val:
  87.                         field_val = False
  88.                 if (
  89.                     isinstance(field_val, models.Model)
  90.                     and field != "tax_ids"
  91.                     and field != "sale_line_ids"
  92.                 ):
  93.                     field_val = field_val.id
  94.                 elif (
  95.                     isinstance(field_val, list)
  96.                     or field == "tax_ids"
  97.                     or field == "sale_line_ids"
  98.                 ):
  99.                     field_val = ((6, 0, tuple(v.id for v in field_val)),)
  100.                 list_key.append((field, field_val))
  101.             list_key.sort()
  102.             return tuple(list_key)
  103.  
  104.         # compute what the new invoices should contain
  105.         new_invoices = {}
  106.         seen_origins = {}
  107.         seen_client_refs = {}
  108.  
  109.         for account_invoice in self._get_draft_invoices():
  110.             invoice_key = make_key(account_invoice, self._get_invoice_key_cols())
  111.             new_invoice = new_invoices.setdefault(invoice_key, ({}, []))
  112.             origins = seen_origins.setdefault(invoice_key, set())
  113.             client_refs = seen_client_refs.setdefault(invoice_key, set())
  114.             new_invoice[1].append(account_invoice.id)
  115.             invoice_infos = new_invoice[0]
  116.             if not invoice_infos:
  117.                 invoice_infos.update(self._get_first_invoice_fields(account_invoice))
  118.                 origins.add(account_invoice.invoice_origin)
  119.                 client_refs.add(account_invoice.payment_reference)
  120.                 if not keep_references:
  121.                     invoice_infos.pop("name")
  122.             else:
  123.                 if account_invoice.name and keep_references:
  124.                     invoice_infos["name"] = (
  125.                         (invoice_infos["name"] or "") + " " + account_invoice.name
  126.                     )
  127.                 if (
  128.                     account_invoice.invoice_origin
  129.                     and account_invoice.invoice_origin not in origins
  130.                 ):
  131.                     invoice_infos["invoice_origin"] = (
  132.                         (invoice_infos["invoice_origin"] or "")
  133.                         + " "
  134.                         + account_invoice.invoice_origin
  135.                     )
  136.                     origins.add(account_invoice.invoice_origin)
  137.                 if (
  138.                     account_invoice.payment_reference
  139.                     and account_invoice.payment_reference not in client_refs
  140.                 ):
  141.                     invoice_infos["payment_reference"] = (
  142.                         (invoice_infos["payment_reference"] or "")
  143.                         + " "
  144.                         + account_invoice.payment_reference
  145.                     )
  146.                     client_refs.add(account_invoice.payment_reference)
  147.  
  148.             for invoice_line in account_invoice.invoice_line_ids:
  149.                 line_key = make_key(invoice_line, self._get_invoice_line_key_cols())
  150.  
  151.                 o_line = invoice_infos["invoice_line_ids"].setdefault(line_key, {})
  152.  
  153.                 if o_line:
  154.                     # merge the line with an existing line
  155.                     o_line["quantity"] += invoice_line.quantity
  156.                 else:
  157.                     # append a new "standalone" line
  158.                     o_line["quantity"] = invoice_line.quantity
  159.  
  160.         allinvoices = []
  161.         allnewinvoices = []
  162.         invoices_info = {}
  163.         old_invoices = self.env["account.move"]
  164.         qty_prec = self.env["decimal.precision"].precision_get(
  165.             "Product Unit of Measure"
  166.         )
  167.         for invoice_key, (invoice_data, old_ids) in new_invoices.items():
  168.             # skip merges with only one invoice
  169.             if len(old_ids) < 2:
  170.                 allinvoices += old_ids or []
  171.                 continue
  172.             # cleanup invoice line data
  173.             for key, value in invoice_data["invoice_line_ids"].items():
  174.                 value.update(dict(key))
  175.  
  176.             if remove_empty_invoice_lines:
  177.                 invoice_data["invoice_line_ids"] = [
  178.                     (0, 0, value)
  179.                     for value in invoice_data["invoice_line_ids"].values()
  180.                     if not float_is_zero(value["quantity"], precision_digits=qty_prec)
  181.                 ]
  182.             else:
  183.                 invoice_data["invoice_line_ids"] = [
  184.                     (0, 0, value) for value in invoice_data["invoice_line_ids"].values()
  185.                 ]
  186.  
  187.             if date_invoice:
  188.                 invoice_data["invoice_date"] = date_invoice
  189.  
  190.             # create the new invoice
  191.             newinvoice = self.with_context(is_merge=True).create(invoice_data)
  192.             invoices_info.update({newinvoice.id: old_ids})
  193.             allinvoices.append(newinvoice.id)
  194.             allnewinvoices.append(newinvoice)
  195.             # cancel old invoices
  196.             old_invoices = self.env["account.move"].browse(old_ids)
  197.             old_invoices.with_context(is_merge=True).button_cancel()
  198.  
  199.         # Make link between original sale order
  200.         # None if sale is not installed
  201.         for new_invoice_id in invoices_info:
  202.             if "sale.order" in self.env.registry:
  203.                 sale_todos = old_invoices.mapped(
  204.                     "invoice_line_ids.sale_line_ids.order_id"
  205.                 )
  206.                 for org_so in sale_todos:
  207.                     for so_line in org_so.order_line:
  208.                         invoice_line = self.env["account.move.line"].search(
  209.                             [
  210.                                 ("id", "in", so_line.invoice_lines.ids),
  211.                                 ("move_id", "=", new_invoice_id),
  212.                             ]
  213.                         )
  214.                         if invoice_line:
  215.                             so_line.write({"invoice_lines": [(6, 0, invoice_line.ids)]})
  216.  
  217.         # recreate link (if any) between original analytic account line
  218.         # (invoice time sheet for example) and this new invoice
  219.         anal_line_obj = self.env["account.analytic.line"]
  220.         if "timesheet_invoice_id" in anal_line_obj._fields:
  221.             for new_invoice_id in invoices_info:
  222.                 anal_todos = anal_line_obj.search(
  223.                     [("timesheet_invoice_id", "in", invoices_info[new_invoice_id])]
  224.                 )
  225.                 anal_todos.write({"timesheet_invoice_id": new_invoice_id})
  226.  
  227.         return invoices_info
Advertisement
Add Comment
Please, Sign In to add comment