('open','Open'),
('paid','Paid'),
('cancel','Cancelled'),
- ],'Status', select=True, readonly=True, track_visibility='onchange',
- help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Invoice. \
- \n* The \'Pro-forma\' when invoice is in Pro-forma status,invoice does not have an invoice number. \
- \n* The \'Open\' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice. \
- \n* The \'Paid\' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled. \
- \n* The \'Cancelled\' status is used when user cancel invoice.'),
- 'sent': fields.boolean('Sent', readonly=True, help="It indicates that the invoice has been sent."),
- 'date_invoice': fields.date('Invoice Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="Keep empty to use the current date"),
- 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True,
- help="If you use payment terms, the due date will be computed automatically at the generation "\
- "of accounting entries. The payment term may compute several due dates, for example 50% now and 50% in one month, but if you want to force a due date, make sure that the payment term is not set on the invoice. If you keep the payment term and the due date empty, it means direct payment."),
- 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}, track_visibility='always'),
- 'payment_term': fields.many2one('account.payment.term', 'Payment Terms',readonly=True, states={'draft':[('readonly',False)]},
- help="If you use payment terms, the due date will be computed automatically at the generation "\
- "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\
- "The payment term may compute several due dates, for example 50% now, 50% in one month."),
- 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}),
-
- 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
- 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
- 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
-
- 'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, select=1, ondelete='restrict', help="Link to the automatically generated Journal Items."),
- 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Subtotal', track_visibility='always',
- store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
- 'account.invoice.tax': (_get_invoice_tax, None, 20),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
- },
- multi='all'),
- 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tax',
- store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
- 'account.invoice.tax': (_get_invoice_tax, None, 20),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
- },
- multi='all'),
- 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total',
- store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
- 'account.invoice.tax': (_get_invoice_tax, None, 20),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
- },
- multi='all'),
- 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}, track_visibility='always'),
- 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]},
- domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]"),
- 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
- 'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean',
- store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
- 'account.move.line': (_get_invoice_from_line, None, 50),
- 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
- }, help="It indicates that the invoice has been paid and the journal entry of the invoice has been reconciled with one or several journal entries of payment."),
- 'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account',
- help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Supplier Refund, otherwise a Partner bank account number.', readonly=True, states={'draft':[('readonly',False)]}),
- 'move_lines':fields.function(_get_lines, type='many2many', relation='account.move.line', string='Entry Lines'),
- 'residual': fields.function(_amount_residual, digits_compute=dp.get_precision('Account'), string='Balance',
- store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','move_id'], 50),
- 'account.invoice.tax': (_get_invoice_tax, None, 50),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 50),
- 'account.move.line': (_get_invoice_from_line, None, 50),
- 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
- },
- help="Remaining amount due."),
- 'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments', groups='base.group_user'),
- 'move_name': fields.char('Journal Entry', size=64, readonly=True, states={'draft':[('readonly',False)]}),
- 'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, track_visibility='onchange', states={'draft':[('readonly',False)]}),
- 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}),
- 'commercial_partner_id': fields.related('partner_id', 'commercial_partner_id', string='Commercial Entity', type='many2one',
- relation='res.partner', store=True, readonly=True,
- help="The commercial entity that will be used on Journal Entries for this invoice")
- }
- _defaults = {
- 'type': _get_type,
- 'state': 'draft',
- 'journal_id': _get_journal,
- 'currency_id': _get_currency,
- 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.invoice', context=c),
- 'reference_type': 'none',
- 'check_total': 0.0,
- 'internal_number': False,
- 'user_id': lambda s, cr, u, c: u,
- 'sent': False,
- }
+ ], string='Status', index=True, readonly=True, default='draft',
+ track_visibility='onchange', copy=False,
+ help=" * The 'Draft' status is used when a user is encoding a new and unconfirmed Invoice.\n"
+ " * The 'Pro-forma' when invoice is in Pro-forma status,invoice does not have an invoice number.\n"
+ " * The 'Open' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice.\n"
+ " * The 'Paid' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled.\n"
+ " * The 'Cancelled' status is used when user cancel invoice.")
+ sent = fields.Boolean(readonly=True, default=False, copy=False,
+ help="It indicates that the invoice has been sent.")
+ date_invoice = fields.Date(string='Invoice Date',
+ readonly=True, states={'draft': [('readonly', False)]}, index=True,
+ help="Keep empty to use the current date", copy=False)
+ date_due = fields.Date(string='Due Date',
+ readonly=True, states={'draft': [('readonly', False)]}, index=True, copy=False,
+ help="If you use payment terms, the due date will be computed automatically at the generation "
+ "of accounting entries. The payment term may compute several due dates, for example 50% "
+ "now and 50% in one month, but if you want to force a due date, make sure that the payment "
+ "term is not set on the invoice. If you keep the payment term and the due date empty, it "
+ "means direct payment.")
+ partner_id = fields.Many2one('res.partner', string='Partner', change_default=True,
+ required=True, readonly=True, states={'draft': [('readonly', False)]},
+ track_visibility='always')
+ payment_term = fields.Many2one('account.payment.term', string='Payment Terms',
+ readonly=True, states={'draft': [('readonly', False)]},
+ help="If you use payment terms, the due date will be computed automatically at the generation "
+ "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "
+ "The payment term may compute several due dates, for example 50% now, 50% in one month.")
+ period_id = fields.Many2one('account.period', string='Force Period',
+ domain=[('state', '!=', 'done')], copy=False,
+ help="Keep empty to use the period of the validation(invoice) date.",
+ readonly=True, states={'draft': [('readonly', False)]})
+
+ account_id = fields.Many2one('account.account', string='Account',
+ required=True, readonly=True, states={'draft': [('readonly', False)]},
+ help="The partner account used for this invoice.")
+ invoice_line = fields.One2many('account.invoice.line', 'invoice_id', string='Invoice Lines',
+ readonly=True, states={'draft': [('readonly', False)]}, copy=True)
+ tax_line = fields.One2many('account.invoice.tax', 'invoice_id', string='Tax Lines',
+ readonly=True, states={'draft': [('readonly', False)]}, copy=True)
+ move_id = fields.Many2one('account.move', string='Journal Entry',
+ readonly=True, index=True, ondelete='restrict', copy=False,
+ help="Link to the automatically generated Journal Items.")
+
+ amount_untaxed = fields.Float(string='Subtotal', digits=dp.get_precision('Account'),
+ store=True, readonly=True, compute='_compute_amount', track_visibility='always')
+ amount_tax = fields.Float(string='Tax', digits=dp.get_precision('Account'),
+ store=True, readonly=True, compute='_compute_amount')
+ amount_total = fields.Float(string='Total', digits=dp.get_precision('Account'),
+ store=True, readonly=True, compute='_compute_amount')
+
+ currency_id = fields.Many2one('res.currency', string='Currency',
+ required=True, readonly=True, states={'draft': [('readonly', False)]},
+ default=_default_currency, track_visibility='always')
+ journal_id = fields.Many2one('account.journal', string='Journal',
+ required=True, readonly=True, states={'draft': [('readonly', False)]},
+ default=_default_journal,
+ domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]")
+ company_id = fields.Many2one('res.company', string='Company', change_default=True,
+ required=True, readonly=True, states={'draft': [('readonly', False)]},
+ default=lambda self: self.env['res.company']._company_default_get('account.invoice'))
+ check_total = fields.Float(string='Verification Total', digits=dp.get_precision('Account'),
+ readonly=True, states={'draft': [('readonly', False)]}, default=0.0)
+
+ reconciled = fields.Boolean(string='Paid/Reconciled',
+ store=True, readonly=True, compute='_compute_reconciled',
+ help="It indicates that the invoice has been paid and the journal entry of the invoice has been reconciled with one or several journal entries of payment.")
+ partner_bank_id = fields.Many2one('res.partner.bank', string='Bank Account',
+ help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Supplier Refund, otherwise a Partner bank account number.',
+ readonly=True, states={'draft': [('readonly', False)]})
+
+ move_lines = fields.Many2many('account.move.line', string='Entry Lines',
+ compute='_compute_move_lines')
+ residual = fields.Float(string='Balance', digits=dp.get_precision('Account'),
+ compute='_compute_residual', store=True,
+ help="Remaining amount due.")
+ payment_ids = fields.Many2many('account.move.line', string='Payments',
+ compute='_compute_payments')
+ move_name = fields.Char(string='Journal Entry', readonly=True,
+ states={'draft': [('readonly', False)]}, copy=False)
+ user_id = fields.Many2one('res.users', string='Salesperson', track_visibility='onchange',
+ readonly=True, states={'draft': [('readonly', False)]},
+ default=lambda self: self.env.user)
+ fiscal_position = fields.Many2one('account.fiscal.position', string='Fiscal Position',
+ readonly=True, states={'draft': [('readonly', False)]})
+ commercial_partner_id = fields.Many2one('res.partner', string='Commercial Entity',
+ related='partner_id.commercial_partner_id', store=True, readonly=True,
+ help="The commercial entity that will be used on Journal Entries for this invoice")
+
_sql_constraints = [
- ('number_uniq', 'unique(number, company_id, journal_id, type)', 'Invoice Number must be unique per Company!'),
+ ('number_uniq', 'unique(number, company_id, journal_id, type)',
+ 'Invoice Number must be unique per Company!'),
]
+ @api.model
+ def fields_view_get(self, view_id=None, view_type=False, toolbar=False, submenu=False):
+ context = self._context
+
++ def get_view_id(xid, name):
++ try:
++ return self.env['ir.model.data'].xmlid_to_res_id('account.' + xid, raise_if_not_found=True)
++ except ValueError:
++ try:
++ return self.env['ir.ui.view'].search([('name', '=', name)], limit=1).id
++ except Exception:
++ return False # view not found
+
-
- def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
- journal_obj = self.pool.get('account.journal')
- if context is None:
- context = {}
-
- if context.get('active_model', '') in ['res.partner'] and context.get('active_ids', False) and context['active_ids']:
- partner = self.pool[context['active_model']].read(cr, uid, context['active_ids'], ['supplier','customer'])[0]
+ if context.get('active_model') == 'res.partner' and context.get('active_ids'):
+ partner = self.env['res.partner'].browse(context['active_ids'])[0]
if not view_type:
- view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.tree')]).id
- try:
- view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_tree')[1]
- except ValueError:
- view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')], limit=1)
++ view_id = get_view_id('invoice_tree', 'account.invoice.tree')
view_type = 'tree'
- if view_type == 'form':
- if partner['supplier'] and not partner['customer']:
- try:
- view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_supplier_form')[1]
- except ValueError:
- view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.supplier.form')], limit=1)
- elif partner['customer'] and not partner['supplier']:
- try:
- view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_form')[1]
- except ValueError:
- view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.form')], limit=1)
- if view_id and isinstance(view_id, (list, tuple)):
- view_id = view_id[0]
- res = super(account_invoice,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
-
- type = context.get('journal_type', False)
+ elif view_type == 'form':
+ if partner.supplier and not partner.customer:
- view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.supplier.form')]).id
++ view_id = get_view_id('invoice_supplier_form', 'account.invoice.supplier.form')
+ elif partner.customer and not partner.supplier:
- view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.form')]).id
++ view_id = get_view_id('invoice_form', 'account.invoice.form')
+
+ res = super(account_invoice, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
+
+ # adapt selection of field journal_id
for field in res['fields']:
if field == 'journal_id' and type:
- journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1)
+ journal_select = self.env['account.journal']._name_search('', [('type', '=', type)], name_get_uid=1)
res['fields'][field]['selection'] = journal_select
doc = etree.XML(res['arch'])