def do_merge(self): """ To merge similar type of account invoices. Invoices will only be merged if: * Account invoices are in draft * Account invoices belong to the same partner * Account invoices are have same company, partner, address, currency, journal, currency, salesman, account, type Lines will only be merged if: * Invoice lines are exactly the same except for the quantity and unit @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: the ID or list of IDs @param context: A standard dictionary @return: new account invoice id """ def make_key(br, fields): list_key = [] for field in fields: field_val = getattr(br, field) if field in ('product_id', 'account_id'): if not field_val: field_val = False if (isinstance(field_val, browse_record) and field != 'invoice_line_tax_id'): field_val = field_val.id elif isinstance(field_val, browse_null): field_val = False elif (isinstance(field_val, list) or field == 'invoice_line_tax_id'): field_val = ((6, 0, tuple([v.id for v in field_val])),) list_key.append((field, field_val)) list_key.sort() return tuple(list_key) # compute what the new invoices should contain new_invoices = {} draft_invoices = [invoice for invoice in self if invoice.state == 'draft'] seen_origins = {} seen_client_refs = {} for account_invoice in draft_invoices: invoice_key = make_key( account_invoice, INVOICE_KEY_COLS) new_invoice = new_invoices.setdefault(invoice_key, ({}, [])) origins = seen_origins.setdefault(invoice_key, set()) client_refs = seen_client_refs.setdefault(invoice_key, set()) new_invoice[1].append(account_invoice.id) invoice_infos = new_invoice[0] if not invoice_infos: invoice_infos.update( self._get_first_invoice_fields(account_invoice)) origins.add(account_invoice.origin) client_refs.add(account_invoice.reference) else: if account_invoice.name: invoice_infos['name'] = \ (invoice_infos['name'] or '') + \ (' %s' % (account_invoice.name,)) if account_invoice.origin and \ account_invoice.origin not in origins: invoice_infos['origin'] = \ (invoice_infos['origin'] or '') + ' ' + \ account_invoice.origin origins.add(account_invoice.origin) if account_invoice.reference \ and account_invoice.reference not in client_refs: invoice_infos['reference'] = \ (invoice_infos['reference'] or '') + \ (' %s' % (account_invoice.reference,)) client_refs.add(account_invoice.reference) for invoice_line in account_invoice.invoice_line: line_key = make_key( invoice_line, INVOICE_LINE_KEY_COLS) o_line = invoice_infos['invoice_line'].setdefault(line_key, {}) uos_factor = (invoice_line.uos_id and invoice_line.uos_id.factor or 1.0) if o_line: # merge the line with an existing line o_line['quantity'] += invoice_line.quantity * \ uos_factor / o_line['uom_factor'] else: # append a new "standalone" line for field in ('quantity', 'uos_id'): field_val = getattr(invoice_line, field) if isinstance(field_val, browse_record): field_val = field_val.id o_line[field] = field_val o_line['uom_factor'] = uos_factor allinvoices = [] invoices_info = {} for invoice_key, (invoice_data, old_ids) in new_invoices.iteritems(): # skip merges with only one invoice if len(old_ids) < 2: allinvoices += (old_ids or []) continue # cleanup invoice line data for key, value in invoice_data['invoice_line'].iteritems(): del value['uom_factor'] value.update(dict(key)) invoice_data['invoice_line'] = [ (0, 0, value) for value in invoice_data['invoice_line'].itervalues()] # create the new invoice newinvoice = self.with_context(is_merge=True).create(invoice_data) invoices_info.update({newinvoice.id: old_ids}) allinvoices.append(newinvoice.id) # make triggers pointing to the old invoices point to the new # invoice for old_id in old_ids: workflow.trg_redirect( self.env.uid, 'account.invoice', old_id, newinvoice.id, self.env.cr) workflow.trg_validate( self.env.uid, 'account.invoice', old_id, 'invoice_cancel', self.env.cr) # make link between original sale order or purchase order # None if sale is not installed so_obj = self.env['sale.order']\ if 'sale.order' in self.env.registry else False invoice_line_obj = self.env['account.invoice.line'] # None if purchase is not installed po_obj = self.env['purchase.order']\ if 'purchase.order' in self.env.registry else False for new_invoice_id in invoices_info: if so_obj: todos = so_obj.search( [('invoice_ids', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_ids': [(4, new_invoice_id)]}) for org_so in todos: for so_line in org_so.order_line: invoice_line_ids = invoice_line_obj.search( [('product_id', '=', so_line.product_id.id), ('invoice_id', '=', new_invoice_id)]) if invoice_line_ids: so_line.write( {'invoice_lines': [(6, 0, invoice_line_ids)]}) if po_obj: todos = po_obj.search( [('invoice_ids', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_ids': [(4, new_invoice_id)]}) # recreate link (if any) between original analytic account line # (invoice time sheet for example) and this new invoice anal_line_obj = self.env['account.analytic.line'] if 'invoice_id' in anal_line_obj._columns: for new_invoice_id in invoices_info: todos = anal_line_obj.search( [('invoice_id', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_id': new_invoice_id}) return invoices_info
def do_merge(self): """ To merge similar type of account invoices. Invoices will only be merged if: * Account invoices are in draft * Account invoices belong to the same partner * Account invoices are have same company, partner, address, currency, journal, currency, salesman, account, type Lines will only be merged if: * Invoice lines are exactly the same except for the quantity and unit @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: the ID or list of IDs @param context: A standard dictionary @return: new account invoice id """ def make_key(br, fields): list_key = [] for field in fields: field_val = getattr(br, field) if field in ('product_id', 'account_id'): if not field_val: field_val = False if (isinstance(field_val, browse_record) and field != 'invoice_line_tax_id'): field_val = field_val.id elif isinstance(field_val, browse_null): field_val = False elif (isinstance(field_val, list) or field == 'invoice_line_tax_id'): field_val = ((6, 0, tuple([v.id for v in field_val])), ) list_key.append((field, field_val)) list_key.sort() return tuple(list_key) # compute what the new invoices should contain new_invoices = {} draft_invoices = [ invoice for invoice in self if invoice.state == 'draft' ] seen_origins = {} seen_client_refs = {} for account_invoice in draft_invoices: invoice_key = make_key(account_invoice, INVOICE_KEY_COLS) new_invoice = new_invoices.setdefault(invoice_key, ({}, [])) origins = seen_origins.setdefault(invoice_key, set()) client_refs = seen_client_refs.setdefault(invoice_key, set()) new_invoice[1].append(account_invoice.id) invoice_infos = new_invoice[0] if not invoice_infos: invoice_infos.update( self._get_first_invoice_fields(account_invoice)) origins.add(account_invoice.origin) client_refs.add(account_invoice.reference) else: if account_invoice.name: invoice_infos['name'] = \ (invoice_infos['name'] or '') + \ (' %s' % (account_invoice.name,)) if account_invoice.origin and \ account_invoice.origin not in origins: invoice_infos['origin'] = \ (invoice_infos['origin'] or '') + ' ' + \ account_invoice.origin origins.add(account_invoice.origin) if account_invoice.reference \ and account_invoice.reference not in client_refs: invoice_infos['reference'] = \ (invoice_infos['reference'] or '') + \ (' %s' % (account_invoice.reference,)) client_refs.add(account_invoice.reference) for invoice_line in account_invoice.invoice_line: line_key = make_key(invoice_line, INVOICE_LINE_KEY_COLS) o_line = invoice_infos['invoice_line'].setdefault(line_key, {}) uos_factor = (invoice_line.uos_id and invoice_line.uos_id.factor or 1.0) if o_line: # merge the line with an existing line o_line['quantity'] += invoice_line.quantity * \ uos_factor / o_line['uom_factor'] else: # append a new "standalone" line for field in ('quantity', 'uos_id'): field_val = getattr(invoice_line, field) if isinstance(field_val, browse_record): field_val = field_val.id o_line[field] = field_val o_line['uom_factor'] = uos_factor allinvoices = [] invoices_info = {} for invoice_key, (invoice_data, old_ids) in new_invoices.iteritems(): # skip merges with only one invoice if len(old_ids) < 2: allinvoices += (old_ids or []) continue # cleanup invoice line data for key, value in invoice_data['invoice_line'].iteritems(): del value['uom_factor'] value.update(dict(key)) invoice_data['invoice_line'] = [ (0, 0, value) for value in invoice_data['invoice_line'].itervalues() ] # create the new invoice newinvoice = self.with_context(is_merge=True).create(invoice_data) invoices_info.update({newinvoice.id: old_ids}) allinvoices.append(newinvoice.id) # make triggers pointing to the old invoices point to the new # invoice for old_id in old_ids: workflow.trg_redirect(self.env.uid, 'account.invoice', old_id, newinvoice.id, self.env.cr) workflow.trg_validate(self.env.uid, 'account.invoice', old_id, 'invoice_cancel', self.env.cr) # make link between original sale order or purchase order # None if sale is not installed so_obj = self.env['sale.order']\ if 'sale.order' in self.env.registry else False invoice_line_obj = self.env['account.invoice.line'] # None if purchase is not installed po_obj = self.env['purchase.order']\ if 'purchase.order' in self.env.registry else False for new_invoice_id in invoices_info: if so_obj: todos = so_obj.search([('invoice_ids', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_ids': [(4, new_invoice_id)]}) for org_so in todos: for so_line in org_so.order_line: invoice_line_ids = invoice_line_obj.search([ ('product_id', '=', so_line.product_id.id), ('invoice_id', '=', new_invoice_id) ]) if invoice_line_ids: so_line.write( {'invoice_lines': [(6, 0, invoice_line_ids)]}) if po_obj: todos = po_obj.search([('invoice_ids', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_ids': [(4, new_invoice_id)]}) # recreate link (if any) between original analytic account line # (invoice time sheet for example) and this new invoice anal_line_obj = self.env['account.analytic.line'] if 'invoice_id' in anal_line_obj._columns: for new_invoice_id in invoices_info: todos = anal_line_obj.search([('invoice_id', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_id': new_invoice_id}) return invoices_info
def do_merge(self, keep_references=True, date_invoice=False): """ To merge similar type of account invoices. Invoices will only be merged if: * Account invoices are in draft * Account invoices belong to the same partner * Account invoices are have same company, partner, address, currency, journal, currency, salesman, account, type Lines will only be merged if: * Invoice lines are exactly the same except for the quantity and unit @param self: The object pointer. @param keep_references: If True, keep reference of original invoices @return: new account invoice id """ def make_key(br, fields): list_key = [] for field in fields: field_val = getattr(br, field) if field in ('product_id', 'account_id'): if not field_val: field_val = False if (isinstance(field_val, browse_record) and field != 'invoice_line_tax_id'): field_val = field_val.id elif isinstance(field_val, browse_null): field_val = False elif (isinstance(field_val, list) or field == 'invoice_line_tax_id'): field_val = ((6, 0, tuple([v.id for v in field_val])), ) list_key.append((field, field_val)) list_key.sort() return tuple(list_key) # compute what the new invoices should contain new_invoices = {} draft_invoices = [ invoice for invoice in self if invoice.state == 'draft' ] seen_origins = {} seen_client_refs = {} for account_invoice in draft_invoices: invoice_key = make_key(account_invoice, self._get_invoice_key_cols()) new_invoice = new_invoices.setdefault(invoice_key, ({}, [])) origins = seen_origins.setdefault(invoice_key, set()) client_refs = seen_client_refs.setdefault(invoice_key, set()) new_invoice[1].append(account_invoice.id) invoice_infos = new_invoice[0] if not invoice_infos: invoice_infos.update( self._get_first_invoice_fields(account_invoice)) origins.add(account_invoice.origin) client_refs.add(account_invoice.reference) if not keep_references: invoice_infos.pop('name') else: if account_invoice.name and keep_references: invoice_infos['name'] = \ (invoice_infos['name'] or '') + \ (' %s' % (account_invoice.name,)) if account_invoice.origin and \ account_invoice.origin not in origins: invoice_infos['origin'] = \ (invoice_infos['origin'] or '') + ' ' + \ account_invoice.origin origins.add(account_invoice.origin) if account_invoice.reference \ and account_invoice.reference not in client_refs: invoice_infos['reference'] = \ (invoice_infos['reference'] or '') + \ (' %s' % (account_invoice.reference,)) client_refs.add(account_invoice.reference) for invoice_line in account_invoice.invoice_line: line_key = make_key(invoice_line, self._get_invoice_line_key_cols()) o_line = invoice_infos['invoice_line'].setdefault(line_key, {}) if o_line: self._merge_invoice_line_values(o_line, invoice_line) o_line['o_line_ids'].append(invoice_line.id) else: # append a new "standalone" line o_line.update( invoice_line._convert_to_write(invoice_line._cache)) del o_line['invoice_id'] o_line['o_line_ids'] = [invoice_line.id] allinvoices = [] invoices_info = {} invoice_lines_info = {} for invoice_key, (invoice_data, old_ids) in new_invoices.iteritems(): # skip merges with only one invoice if len(old_ids) < 2: allinvoices += (old_ids or []) continue if date_invoice: invoice_data['date_invoice'] = date_invoice # create the new invoice invoice_line_data = invoice_data['invoice_line'] del invoice_data['invoice_line'] newinvoice = self.with_context(is_merge=True).create(invoice_data) invoice_lines_info[newinvoice.id] = {} for entry in invoice_line_data.values(): o_line_ids = entry['o_line_ids'] del entry['o_line_ids'] entry['invoice_id'] = newinvoice.id inv_line = self.env['account.invoice.line'].create(entry) for o_line_id in o_line_ids: invoice_lines_info[newinvoice.id][o_line_id] = inv_line.id newinvoice.button_reset_taxes() invoices_info.update({newinvoice.id: old_ids}) allinvoices.append(newinvoice.id) # make triggers pointing to the old invoices point to the new # invoice for old_id in old_ids: workflow.trg_redirect(self.env.uid, 'account.invoice', old_id, newinvoice.id, self.env.cr) workflow.trg_validate(self.env.uid, 'account.invoice', old_id, 'invoice_cancel', self.env.cr) # make link between original sale order if sale is not installed # None if purchase is not installed if 'sale.order' in self.env.registry: so_obj = self.env['sale.order'] for new_invoice_id in invoices_info: todos = so_obj.search([('invoice_ids', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_ids': [(4, new_invoice_id)]}) for org_so in todos: for so_line in org_so.order_line: org_ilines = so_line.mapped('invoice_lines') invoice_line_ids = [] for org_iline in org_ilines: invoice_line_ids.append( invoice_lines_info[new_invoice_id][ org_iline.id]) so_line.write( {'invoice_lines': [(6, 0, invoice_line_ids)]}) # recreate link (if any) between original analytic account line # (invoice time sheet for example) and this new invoice anal_line_obj = self.env['account.analytic.line'] if 'invoice_id' in anal_line_obj._columns: for new_invoice_id in invoices_info: todos = anal_line_obj.search([('invoice_id', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_id': new_invoice_id}) return invoices_info, invoice_lines_info
def do_merge(self, keep_references=True, date_invoice=False, remove_empty_invoice_lines=True): """ To merge similar type of account invoices. Invoices will only be merged if: * Account invoices are in draft * Account invoices belong to the same partner * Account invoices are have same company, partner, address, currency, journal, currency, salesman, account, type Lines will only be merged if: * Invoice lines are exactly the same except for the quantity and unit @param self: The object pointer. @param keep_references: If True, keep reference of original invoices @return: new account invoice id """ def make_key(br, fields): list_key = [] for field in fields: field_val = getattr(br, field) if field in ('product_id', 'account_id'): if not field_val: field_val = False if (isinstance(field_val, browse_record) and field != 'invoice_line_tax_ids' and field != 'sale_line_ids'): field_val = field_val.id elif isinstance(field_val, browse_null): field_val = False elif (isinstance(field_val, list) or field == 'invoice_line_tax_ids' or field == 'sale_line_ids'): field_val = ((6, 0, tuple([v.id for v in field_val])), ) list_key.append((field, field_val)) list_key.sort() return tuple(list_key) # compute what the new invoices should contain new_invoices = {} draft_invoices = [ invoice for invoice in self if invoice.state == 'draft' ] seen_origins = {} seen_client_refs = {} for account_invoice in draft_invoices: invoice_key = make_key(account_invoice, self._get_invoice_key_cols()) new_invoice = new_invoices.setdefault(invoice_key, ({}, [])) origins = seen_origins.setdefault(invoice_key, set()) client_refs = seen_client_refs.setdefault(invoice_key, set()) new_invoice[1].append(account_invoice.id) invoice_infos = new_invoice[0] if not invoice_infos: invoice_infos.update( self._get_first_invoice_fields(account_invoice)) origins.add(account_invoice.origin) client_refs.add(account_invoice.reference) if not keep_references: invoice_infos.pop('name') else: if account_invoice.name and keep_references: invoice_infos['name'] = \ (invoice_infos['name'] or '') + ' ' + \ account_invoice.name if account_invoice.origin and \ account_invoice.origin not in origins: invoice_infos['origin'] = \ (invoice_infos['origin'] or '') + ' ' + \ account_invoice.origin origins.add(account_invoice.origin) if account_invoice.reference \ and account_invoice.reference not in client_refs: invoice_infos['reference'] = \ (invoice_infos['reference'] or '') + ' ' + \ account_invoice.reference client_refs.add(account_invoice.reference) for invoice_line in account_invoice.invoice_line_ids: line_key = make_key(invoice_line, self._get_invoice_line_key_cols()) o_line = invoice_infos['invoice_line_ids'].\ setdefault(line_key, {}) if o_line: # merge the line with an existing line o_line['quantity'] += invoice_line.quantity else: # append a new "standalone" line o_line['quantity'] = invoice_line.quantity allinvoices = [] allnewinvoices = [] invoices_info = {} qty_prec = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') for invoice_key, (invoice_data, old_ids) in new_invoices.iteritems(): # skip merges with only one invoice if len(old_ids) < 2: allinvoices += (old_ids or []) continue # cleanup invoice line data for key, value in invoice_data['invoice_line_ids'].iteritems(): value.update(dict(key)) if remove_empty_invoice_lines: invoice_data['invoice_line_ids'] = [ (0, 0, value) for value in invoice_data['invoice_line_ids'].itervalues() if not float_is_zero(value['quantity'], precision_digits=qty_prec) ] else: invoice_data['invoice_line_ids'] = [ (0, 0, value) for value in invoice_data['invoice_line_ids'].itervalues() ] if date_invoice: invoice_data['date_invoice'] = date_invoice # create the new invoice newinvoice = self.with_context(is_merge=True).create(invoice_data) invoices_info.update({newinvoice.id: old_ids}) allinvoices.append(newinvoice.id) allnewinvoices.append(newinvoice) # make triggers pointing to the old invoices point to the new # invoice for old_id in old_ids: workflow.trg_redirect(self.env.uid, 'account.invoice', old_id, newinvoice.id, self.env.cr) workflow.trg_validate(self.env.uid, 'account.invoice', old_id, 'invoice_cancel', self.env.cr) # Make link between original sale order # None if sale is not installed so_line_obj = self.env['sale.order.line'] \ if 'sale.order' in self.env.registry else False invoice_line_obj = self.env['account.invoice.line'] for new_invoice_id in invoices_info: if so_line_obj is not False: so_lines = so_line_obj.search([ ('invoice_lines.invoice_id', 'in', invoices_info[new_invoice_id]) ]) for so_line in so_lines: invoice_lines = invoice_line_obj.search([ ('product_id', '=', so_line.product_id.id), ('invoice_id', '=', new_invoice_id) ]) if invoice_lines: so_line.write( {'invoice_lines': [(6, 0, invoice_lines.ids)]}) # recreate link (if any) between original analytic account line # (invoice time sheet for example) and this new invoice anal_line_obj = self.env['account.analytic.line'] if 'invoice_id' in anal_line_obj._columns: for new_invoice_id in invoices_info: todos = anal_line_obj.search([('invoice_id', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_id': new_invoice_id}) for new_invoice in allnewinvoices: new_invoice.compute_taxes() return invoices_info