diff --git a/addons/l10n_id_efaktur/models/account_move.py b/addons/l10n_id_efaktur/models/account_move.py
index 7ffe407aeed..d810e77a13b 100644
--- a/addons/l10n_id_efaktur/models/account_move.py
+++ b/addons/l10n_id_efaktur/models/account_move.py
@@ -136,9 +136,6 @@ class AccountMove(models.Model):
def _generate_efaktur_invoice(self, delimiter):
"""Generate E-Faktur for customer invoice."""
# Invoice of Customer
- company_id = self.company_id
- dp_product_id = self.env['ir.config_parameter'].sudo().get_param('sale.default_deposit_product_id')
-
output_head = '%s%s%s' % (
_csv_row(FK_HEAD_LIST, delimiter),
_csv_row(LT_HEAD_LIST, delimiter),
@@ -180,7 +177,7 @@ class AccountMove(models.Model):
eTax['REFERENSI'] = number_ref
eTax['KODE_DOKUMEN_PENDUKUNG'] = '0'
- lines = move.line_ids.filtered(lambda x: x.product_id.id == int(dp_product_id) and x.price_unit < 0 and x.display_type == 'product')
+ lines = move.line_ids.filtered(lambda x: x.is_downpayment and x.price_unit < 0 and not x.display_type)
eTax['FG_UANG_MUKA'] = 0
eTax['UANG_MUKA_DPP'] = int(abs(sum(lines.mapped(lambda l: float_round(l.price_subtotal, 0)))))
eTax['UANG_MUKA_PPN'] = int(abs(sum(lines.mapped(lambda l: float_round(l.price_total - l.price_subtotal, 0)))))
diff --git a/addons/l10n_in_sale/wizard/sale_make_invoice_advance.py b/addons/l10n_in_sale/wizard/sale_make_invoice_advance.py
index 6fc5f349c04..f22ba445d1b 100644
--- a/addons/l10n_in_sale/wizard/sale_make_invoice_advance.py
+++ b/addons/l10n_in_sale/wizard/sale_make_invoice_advance.py
@@ -7,8 +7,8 @@ from odoo import models
class SaleAdvancePaymentInv(models.TransientModel):
_inherit = "sale.advance.payment.inv"
- def _prepare_invoice_values(self, order, name, amount, so_line):
- res = super()._prepare_invoice_values(order, name, amount, so_line)
+ def _prepare_invoice_values(self, order, so_line):
+ res = super()._prepare_invoice_values(order, so_line)
if order.l10n_in_journal_id:
res['journal_id'] = order.l10n_in_journal_id.id
if order.country_code == 'IN':
diff --git a/addons/sale/models/account_move_line.py b/addons/sale/models/account_move_line.py
index 413abc9265d..6cb4f6d4e35 100644
--- a/addons/sale/models/account_move_line.py
+++ b/addons/sale/models/account_move_line.py
@@ -9,6 +9,7 @@ from odoo.tools import float_compare, float_is_zero
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
+ is_downpayment = fields.Boolean()
sale_line_ids = fields.Many2many(
'sale.order.line',
'sale_order_line_invoice_rel',
diff --git a/addons/sale/models/sale_order.py b/addons/sale/models/sale_order.py
index 3e48e983f1d..1d6e480bb68 100644
--- a/addons/sale/models/sale_order.py
+++ b/addons/sale/models/sale_order.py
@@ -947,28 +947,26 @@ class SaleOrder(models.Model):
"""
self.ensure_one()
- invoice_vals = {
+ return {
'ref': self.client_order_ref or '',
'move_type': 'out_invoice',
'narration': self.note,
- 'currency_id': self.pricelist_id.currency_id.id,
+ 'currency_id': self.currency_id.id,
'campaign_id': self.campaign_id.id,
'medium_id': self.medium_id.id,
'source_id': self.source_id.id,
- 'user_id': self.user_id.id,
- 'invoice_user_id': self.user_id.id,
'team_id': self.team_id.id,
'partner_id': self.partner_invoice_id.id,
'partner_shipping_id': self.partner_shipping_id.id,
'fiscal_position_id': (self.fiscal_position_id or self.fiscal_position_id._get_fiscal_position(self.partner_invoice_id)).id,
'invoice_origin': self.name,
'invoice_payment_term_id': self.payment_term_id.id,
+ 'invoice_user_id': self.user_id.id,
'payment_reference': self.reference,
'transaction_ids': [Command.set(self.transaction_ids.ids)],
- 'invoice_line_ids': [],
'company_id': self.company_id.id,
+ 'invoice_line_ids': [],
}
- return invoice_vals
def action_view_invoice(self):
invoices = self.mapped('invoice_ids')
diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py
index fad44e22147..ea6b1532503 100644
--- a/addons/sale/models/sale_order_line.py
+++ b/addons/sale/models/sale_order_line.py
@@ -5,6 +5,7 @@ from datetime import timedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
+from odoo.fields import Command
from odoo.osv import expression
from odoo.tools import float_is_zero, float_compare, float_round
@@ -1043,11 +1044,10 @@ class SaleOrderLine(models.Model):
return new or old
def _prepare_invoice_line(self, **optional_values):
- """
- Prepare the dict of values to create the new invoice line for a sales order line.
+ """Prepare the values to create the new invoice line for a sales order line.
- :param qty: float quantity to invoice
:param optional_values: any parameter that should be added to the returned invoice line
+ :rtype: dict
"""
self.ensure_one()
res = {
@@ -1059,9 +1059,10 @@ class SaleOrderLine(models.Model):
'quantity': self.qty_to_invoice,
'discount': self.discount,
'price_unit': self.price_unit,
- 'tax_ids': [(6, 0, self.tax_id.ids)],
- 'analytic_tag_ids': [(6, 0, self.analytic_tag_ids.ids)],
- 'sale_line_ids': [(4, self.id)],
+ 'tax_ids': [Command.set(self.tax_id.ids)],
+ 'analytic_tag_ids': [Command.set(self.analytic_tag_ids.ids)],
+ 'sale_line_ids': [Command.link(self.id)],
+ 'is_downpayment': self.is_downpayment,
}
if self.order_id.analytic_account_id:
res['analytic_account_id'] = self.order_id.analytic_account_id.id
diff --git a/addons/sale/tests/test_sale_refund.py b/addons/sale/tests/test_sale_refund.py
index a20964bc885..ccb0b6c2fc5 100644
--- a/addons/sale/tests/test_sale_refund.py
+++ b/addons/sale/tests/test_sale_refund.py
@@ -329,7 +329,19 @@ class TestSaleRefund(TestSaleCommon):
'tax_id': False,
})
+ self.assertRecordValues(sol_product, [{
+ 'price_unit': 280.0,
+ 'discount': 0.0,
+ 'product_uom_qty': 5.0,
+ 'qty_to_invoice': 0.0,
+ 'invoice_status': 'no',
+ }])
+
sale_order_refund.action_confirm()
+
+ self.assertEqual(sol_product.qty_to_invoice, 5.0)
+ self.assertEqual(sol_product.invoice_status, 'to invoice')
+
so_context = {
'active_model': 'sale.order',
'active_ids': [sale_order_refund.id],
@@ -343,9 +355,21 @@ class TestSaleRefund(TestSaleCommon):
'deposit_account_id': self.company_data['default_account_revenue'].id
})
downpayment.create_invoices()
- sale_order_refund.invoice_ids[0].action_post()
# order_line[1] is the down payment section
sol_downpayment = sale_order_refund.order_line[2]
+ dp_invoice = sale_order_refund.invoice_ids[0]
+ dp_invoice.action_post()
+
+ self.assertRecordValues(sol_downpayment, [{
+ 'price_unit': 700.0,
+ 'discount': 0.0,
+ 'invoice_status': 'to invoice',
+ 'untaxed_amount_to_invoice': -700.0,
+ 'untaxed_amount_invoiced': 700.0,
+ 'product_uom_qty': 0.0,
+ 'qty_invoiced': 1.0,
+ 'qty_to_invoice': -1.0,
+ }])
payment = self.env['sale.advance.payment.inv'].with_context(so_context).create({
'deposit_account_id': self.company_data['default_account_revenue'].id
diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py
index 04eb414df79..dd557d6f7a8 100644
--- a/addons/sale/wizard/sale_make_invoice_advance.py
+++ b/addons/sale/wizard/sale_make_invoice_advance.py
@@ -5,138 +5,205 @@ import time
from odoo import api, fields, models, _
from odoo.exceptions import UserError
+from odoo.fields import Command
class SaleAdvancePaymentInv(models.TransientModel):
- _name = "sale.advance.payment.inv"
+ _name = 'sale.advance.payment.inv'
_description = "Sales Advance Payment Invoice"
- @api.model
- def _count(self):
- return len(self._context.get('active_ids', []))
+ advance_payment_method = fields.Selection(
+ selection=[
+ ('delivered', "Regular invoice"),
+ ('percentage', "Down payment (percentage)"),
+ ('fixed', "Down payment (fixed amount)"),
+ ],
+ string="Create Invoice",
+ default='delivered',
+ required=True,
+ help="A standard invoice is issued with all the order lines ready for invoicing,"
+ "according to their invoicing policy (based on ordered or delivered quantity).")
+ count = fields.Integer(string="Order Count", compute='_compute_count')
+ sale_order_ids = fields.Many2many(
+ 'sale.order', default=lambda self: self.env.context.get('active_ids'))
- @api.model
- def _default_product_id(self):
- product_id = self.env['ir.config_parameter'].sudo().get_param('sale.default_deposit_product_id')
- return self.env['product.product'].browse(int(product_id)).exists()
+ # Down Payment logic
+ has_down_payments = fields.Boolean(
+ string="Has down payments", compute="_compute_has_down_payments")
+ deduct_down_payments = fields.Boolean(string="Deduct down payments", default=True)
- @api.model
- def _default_deposit_account_id(self):
- return self._default_product_id()._get_product_accounts()['income']
+ # New Down Payment
+ product_id = fields.Many2one(
+ comodel_name='product.product',
+ string="Down Payment Product",
+ domain=[('type', '=', 'service')],
+ compute='_compute_product_id',
+ readonly=False,
+ store=True)
+ amount = fields.Float(
+ string="Down Payment Amount",
+ help="The percentage of amount to be invoiced in advance, taxes excluded.")
+ fixed_amount = fields.Monetary(
+ string="Down Payment Amount (Fixed)",
+ help="The fixed amount to be invoiced in advance, taxes excluded.")
+ currency_id = fields.Many2one(
+ comodel_name='res.currency',
+ compute='_compute_currency_id',
+ store=True)
+ company_id = fields.Many2one(
+ comodel_name='res.company',
+ compute='_compute_company_id',
+ store=True)
- @api.model
- def _default_deposit_taxes_id(self):
- return self._default_product_id().taxes_id
+ # Only used when there is no down payment product available
+ # to setup the down payment product
+ deposit_account_id = fields.Many2one(
+ comodel_name='account.account',
+ string="Income Account",
+ domain=[('deprecated', '=', False)],
+ help="Account used for deposits")
+ deposit_taxes_id = fields.Many2many(
+ comodel_name='account.tax',
+ string="Customer Taxes",
+ domain=[('type_tax_use', '=', 'sale')],
+ help="Taxes used for deposits")
- @api.model
- def _default_has_down_payment(self):
- if self._context.get('active_model') == 'sale.order' and self._context.get('active_id', False):
- sale_order = self.env['sale.order'].browse(self._context.get('active_id'))
- return sale_order.order_line.filtered(
- lambda sale_order_line: sale_order_line.is_downpayment
+ #=== COMPUTE METHODS ===#
+
+ @api.depends('sale_order_ids')
+ def _compute_count(self):
+ for wizard in self:
+ wizard.count = len(wizard.sale_order_ids)
+
+ @api.depends('sale_order_ids')
+ def _compute_has_down_payments(self):
+ for wizard in self:
+ wizard.has_down_payments = bool(
+ wizard.sale_order_ids.order_line.filtered('is_downpayment')
)
- return False
+ # next computed fields are only used for down payments invoices and therefore should only
+ # have a value when 1 unique SO is invoiced through the wizard
+ @api.depends('sale_order_ids')
+ def _compute_currency_id(self):
+ self.currency_id = False
+ for wizard in self:
+ if wizard.count == 1:
+ wizard.currency_id = wizard.sale_order_ids.currency_id
- @api.model
- def _default_currency_id(self):
- if self._context.get('active_model') == 'sale.order' and self._context.get('active_id', False):
- sale_order = self.env['sale.order'].browse(self._context.get('active_id'))
- return sale_order.currency_id
+ @api.depends('sale_order_ids')
+ def _compute_company_id(self):
+ self.company_id = False
+ for wizard in self:
+ if wizard.count == 1:
+ wizard.company_id = wizard.sale_order_ids.company_id
- advance_payment_method = fields.Selection([
- ('delivered', 'Regular invoice'),
- ('percentage', 'Down payment (percentage)'),
- ('fixed', 'Down payment (fixed amount)')
- ], string='Create Invoice', default='delivered', required=True,
- help="A standard invoice is issued with all the order lines ready for invoicing, \
- according to their invoicing policy (based on ordered or delivered quantity).")
- deduct_down_payments = fields.Boolean('Deduct down payments', default=True)
- has_down_payments = fields.Boolean('Has down payments', default=_default_has_down_payment, readonly=True)
- product_id = fields.Many2one('product.product', string='Down Payment Product', domain=[('type', '=', 'service')],
- default=_default_product_id)
- count = fields.Integer(default=_count, string='Order Count')
- amount = fields.Float('Down Payment Amount', digits='Account', help="The percentage of amount to be invoiced in advance, taxes excluded.")
- currency_id = fields.Many2one('res.currency', string='Currency', default=_default_currency_id)
- fixed_amount = fields.Monetary('Down Payment Amount (Fixed)', help="The fixed amount to be invoiced in advance, taxes excluded.")
- deposit_account_id = fields.Many2one("account.account", string="Income Account", domain=[('deprecated', '=', False)],
- help="Account used for deposits", default=_default_deposit_account_id)
- deposit_taxes_id = fields.Many2many("account.tax", string="Customer Taxes", help="Taxes used for deposits", default=_default_deposit_taxes_id)
+ @api.depends('company_id') # 'dumb' depends to trigger the computation
+ def _compute_product_id(self):
+ self.product_id = False
+ dp_product_id = int(self.env['ir.config_parameter'].sudo().get_param(
+ 'sale.default_deposit_product_id'))
+ if not dp_product_id:
+ return
+ for wizard in self:
+ if wizard.count == 1:
+ wizard.product_id = dp_product_id
+
+ #=== ONCHANGE METHODS ===#
@api.onchange('advance_payment_method')
- def onchange_advance_payment_method(self):
+ def _onchange_advance_payment_method(self):
if self.advance_payment_method == 'percentage':
amount = self.default_get(['amount']).get('amount')
return {'value': {'amount': amount}}
- return {}
- def _prepare_invoice_values(self, order, name, amount, so_line):
- invoice_vals = {
- 'ref': order.client_order_ref,
- 'move_type': 'out_invoice',
- 'invoice_origin': order.name,
- 'invoice_user_id': order.user_id.id,
- 'narration': order.note,
- 'partner_id': order.partner_invoice_id.id,
- 'fiscal_position_id': (order.fiscal_position_id or order.fiscal_position_id._get_fiscal_position(order.partner_id)).id,
- 'partner_shipping_id': order.partner_shipping_id.id,
- 'currency_id': order.pricelist_id.currency_id.id,
- 'payment_reference': order.reference,
- 'invoice_payment_term_id': order.payment_term_id.id,
- 'partner_bank_id': order.company_id.partner_id.bank_ids[:1].id,
- 'team_id': order.team_id.id,
- 'campaign_id': order.campaign_id.id,
- 'medium_id': order.medium_id.id,
- 'source_id': order.source_id.id,
- 'invoice_line_ids': [(0, 0, {
- 'name': name,
- 'price_unit': amount,
- 'quantity': 1.0,
- 'product_id': self.product_id.id,
- 'product_uom_id': so_line.product_uom.id,
- 'tax_ids': [(6, 0, so_line.tax_id.ids)],
- 'sale_line_ids': [(6, 0, [so_line.id])],
- 'analytic_tag_ids': [(6, 0, so_line.analytic_tag_ids.ids)],
- 'analytic_account_id': order.analytic_account_id.id or False,
- })],
+ #=== CONSTRAINT METHODS ===#
+
+ @api.constrains('advance_payment_method', 'amount', 'fixed_amount')
+ def _check_amount_is_positive(self):
+ for wizard in self:
+ if wizard.advance_payment_method == 'percentage' and wizard.amount <= 0.00:
+ raise UserError(_('The value of the down payment amount must be positive.'))
+ elif wizard.advance_payment_method == 'fixed' and wizard.fixed_amount <= 0.00:
+ raise UserError(_('The value of the down payment amount must be positive.'))
+
+ @api.constrains('product_id')
+ def _check_down_payment_product_is_valid(self):
+ for wizard in self:
+ if wizard.count > 1 or not wizard.product_id:
+ continue
+ if wizard.product_id.invoice_policy != 'order':
+ raise UserError(_(
+ "The product used to invoice a down payment should have an invoice policy"
+ "set to \"Ordered quantities\"."
+ " Please update your deposit product to be able to create a deposit invoice."))
+ if wizard.product_id.type != 'service':
+ raise UserError(_(
+ "The product used to invoice a down payment should be of type 'Service'."
+ " Please use another product or update this product."))
+
+ #=== ACTION METHODS ===#
+
+ def create_invoices(self):
+ self._create_invoices(self.sale_order_ids)
+
+ if self.env.context.get('open_invoices'):
+ return self.sale_order_ids.action_view_invoice()
+
+ return {'type': 'ir.actions.act_window_close'}
+
+ #=== BUSINESS METHODS ===#
+
+ def _create_invoices(self, sale_orders):
+ self.ensure_one()
+ if self.advance_payment_method == 'delivered':
+ return sale_orders._create_invoices(final=self.deduct_down_payments)
+ else:
+ self.sale_order_ids.ensure_one()
+ self = self.with_company(self.company_id)
+ order = self.sale_order_ids
+
+ # Create deposit product if necessary
+ if not self.product_id:
+ self.product_id = self.env['product.product'].create(
+ self._prepare_down_payment_product_values()
+ )
+ self.env['ir.config_parameter'].sudo().set_param(
+ 'sale.default_deposit_product_id', self.product_id.id)
+
+ # Create down payment section if necessary
+ if not any(line.display_type and line.is_downpayment for line in order.order_line):
+ self.env['sale.order.line'].create(
+ self._prepare_down_payment_section_values(order)
+ )
+
+ down_payment_so_line = self.env['sale.order.line'].create(
+ self._prepare_so_line_values(order)
+ )
+
+ invoice = self.env['account.move'].sudo().create(
+ self._prepare_invoice_values(order, down_payment_so_line)
+ ).with_user(self.env.uid) # Unsudo the invoice after creation
+
+ invoice.message_post_with_view(
+ 'mail.message_origin_link',
+ values={'self': invoice, 'origin': order},
+ subtype_id=self.env.ref('mail.mt_note').id)
+
+ return invoice
+
+ def _prepare_down_payment_product_values(self):
+ self.ensure_one()
+ return {
+ 'name': _('Down payment'),
+ 'type': 'service',
+ 'invoice_policy': 'order',
+ 'company_id': False,
+ 'property_account_income_id': self.deposit_account_id.id,
+ 'taxes_id': [Command.set(self.deposit_taxes_id.ids)],
}
- return invoice_vals
-
- def _get_advance_details(self, order):
- context = {'lang': order.partner_id.lang}
- if self.advance_payment_method == 'percentage':
- if all(self.product_id.taxes_id.mapped('price_include')):
- amount = order.amount_total * self.amount / 100
- else:
- amount = order.amount_untaxed * self.amount / 100
- name = _("Down payment of %s%%") % (self.amount)
- else:
- amount = self.fixed_amount
- name = _('Down Payment')
- del context
-
- return amount, name
-
- def _create_invoice(self, order, so_line, amount):
- if (self.advance_payment_method == 'percentage' and self.amount <= 0.00) or (self.advance_payment_method == 'fixed' and self.fixed_amount <= 0.00):
- raise UserError(_('The value of the down payment amount must be positive.'))
-
- amount, name = self._get_advance_details(order)
-
- invoice_vals = self._prepare_invoice_values(order, name, amount, so_line)
-
- if order.fiscal_position_id:
- invoice_vals['fiscal_position_id'] = order.fiscal_position_id.id
-
- invoice = self.env['account.move'].with_company(order.company_id)\
- .sudo().create(invoice_vals).with_user(self.env.uid)
- invoice.message_post_with_view('mail.message_origin_link',
- values={'self': invoice, 'origin': order},
- subtype_id=self.env.ref('mail.mt_note').id)
- return invoice
-
- def _prepare_downpayment_section(self, order):
+ def _prepare_down_payment_section_values(self, order):
context = {'lang': order.partner_id.lang}
so_values = {
@@ -151,72 +218,59 @@ class SaleAdvancePaymentInv(models.TransientModel):
del context
return so_values
- def _prepare_so_line(self, order, analytic_tag_ids, tax_ids, amount):
+ def _prepare_so_line_values(self, order):
+ self.ensure_one()
context = {'lang': order.partner_id.lang}
so_values = {
- 'name': _('Down Payment: %s (Draft)') % (time.strftime('%m %Y'),),
- 'price_unit': amount,
+ 'name': _('Down Payment: %s (Draft)', time.strftime('%m %Y')),
+ 'price_unit': self._get_down_payment_amount(order),
'product_uom_qty': 0.0,
'order_id': order.id,
'discount': 0.0,
- 'product_uom': self.product_id.uom_id.id,
'product_id': self.product_id.id,
- 'analytic_tag_ids': analytic_tag_ids,
- 'tax_id': [(6, 0, tax_ids)],
+ 'analytic_tag_ids': [
+ Command.link(analytic_tag.id)
+ for line in order.order_line
+ for analytic_tag in line.analytic_tag_ids
+ ],
'is_downpayment': True,
'sequence': order.order_line and order.order_line[-1].sequence + 1 or 10,
}
del context
return so_values
- def _create_invoices(self, sale_orders):
- if self.advance_payment_method == 'delivered':
- return sale_orders._create_invoices(final=self.deduct_down_payments)
- else:
- # Create deposit product if necessary
- if not self.product_id:
- vals = self._prepare_deposit_product()
- self.product_id = self.env['product.product'].create(vals)
- self.env['ir.config_parameter'].sudo().set_param('sale.default_deposit_product_id', self.product_id.id)
+ def _get_down_payment_amount(self, order):
+ self.ensure_one()
+ if self.advance_payment_method == 'percentage':
+ if all(self.product_id.taxes_id.mapped('price_include')):
+ amount = order.amount_total * self.amount / 100
+ else:
+ amount = order.amount_untaxed * self.amount / 100
+ else: # Fixed amount
+ amount = self.fixed_amount
+ return amount
- sale_line_obj = self.env['sale.order.line']
- invoices = self.env['account.move']
- for order in sale_orders:
- amount, name = self._get_advance_details(order)
-
- if self.product_id.invoice_policy != 'order':
- raise UserError(_('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'))
- if self.product_id.type != 'service':
- raise UserError(_("The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product."))
- taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id)
- tax_ids = order.fiscal_position_id.map_tax(taxes).ids
- analytic_tag_ids = []
- for line in order.order_line:
- analytic_tag_ids = [(4, analytic_tag.id, None) for analytic_tag in line.analytic_tag_ids]
-
- # verify that no down payment section already exists
- if not any(line.display_type and line.is_downpayment for line in order.order_line):
- so_downpayment_section_values = self._prepare_downpayment_section(order)
- sale_line_obj.create(so_downpayment_section_values)
-
- so_line_values = self._prepare_so_line(order, analytic_tag_ids, tax_ids, amount)
- so_line = sale_line_obj.create(so_line_values)
- invoices += self._create_invoice(order, so_line, amount)
- return invoices
-
- def create_invoices(self):
- sale_orders = self.env['sale.order'].browse(self._context.get('active_ids', []))
- self._create_invoices(sale_orders)
- if self._context.get('open_invoices', False):
- return sale_orders.action_view_invoice()
- return {'type': 'ir.actions.act_window_close'}
-
- def _prepare_deposit_product(self):
+ def _prepare_invoice_values(self, order, so_line):
+ self.ensure_one()
return {
- 'name': _('Down payment'),
- 'type': 'service',
- 'invoice_policy': 'order',
- 'property_account_income_id': self.deposit_account_id.id,
- 'taxes_id': [(6, 0, self.deposit_taxes_id.ids)],
- 'company_id': False,
+ **order._prepare_invoice(),
+ 'invoice_line_ids': [
+ Command.create(
+ so_line._prepare_invoice_line(
+ name=self._get_down_payment_description(order),
+ quantity=1.0,
+ )
+ )
+ ],
}
+
+ def _get_down_payment_description(self, order):
+ self.ensure_one()
+ context = {'lang': order.partner_id.lang}
+ if self.advance_payment_method == 'percentage':
+ name = _("Down payment of %s%%", self.amount)
+ else:
+ name = _('Down Payment')
+ del context
+
+ return name
diff --git a/addons/sale/wizard/sale_make_invoice_advance_views.xml b/addons/sale/wizard/sale_make_invoice_advance_views.xml
index 6cd67ddb172..48a58c4742e 100644
--- a/addons/sale/wizard/sale_make_invoice_advance_views.xml
+++ b/addons/sale/wizard/sale_make_invoice_advance_views.xml
@@ -1,64 +1,81 @@
-
- Invoice Orders
- sale.advance.payment.inv
-
-
-
-
-
- Create invoices
- ir.actions.act_window
- sale.advance.payment.inv
- form
- new
-
-
- list
-
+
+ Invoice Orders
+ sale.advance.payment.inv
+
+
+
+
+
+
+ Create invoices
+ ir.actions.act_window
+ sale.advance.payment.inv
+ form
+ new
+
+ list
+
diff --git a/addons/sale_timesheet/wizard/sale_make_invoice_advance.py b/addons/sale_timesheet/wizard/sale_make_invoice_advance.py
index f48b4487387..967ed5859f1 100644
--- a/addons/sale_timesheet/wizard/sale_make_invoice_advance.py
+++ b/addons/sale_timesheet/wizard/sale_make_invoice_advance.py
@@ -5,24 +5,30 @@ from odoo import api, fields, models
class SaleAdvancePaymentInv(models.TransientModel):
- _inherit = "sale.advance.payment.inv"
-
- @api.model
- def _default_invoicing_timesheet_enabled(self):
- if 'active_id' not in self._context and 'active_ids' not in self._context:
- return False
- sale_orders = self.env['sale.order'].browse(self._context.get('active_id') or self._context.get('active_ids'))
- order_lines = sale_orders.mapped('order_line').filtered(lambda sol: sol.invoice_status == 'to invoice')
- product_ids = order_lines.mapped('product_id').filtered(lambda p: p._is_delivered_timesheet())
- return bool(product_ids)
+ _inherit = 'sale.advance.payment.inv'
date_start_invoice_timesheet = fields.Date(
- string='Start Date',
+ string="Start Date",
help="Only timesheets not yet invoiced (and validated, if applicable) from this period will be invoiced. If the period is not indicated, all timesheets not yet invoiced (and validated, if applicable) will be invoiced without distinction.")
date_end_invoice_timesheet = fields.Date(
- string='End Date',
+ string="End Date",
help="Only timesheets not yet invoiced (and validated, if applicable) from this period will be invoiced. If the period is not indicated, all timesheets not yet invoiced (and validated, if applicable) will be invoiced without distinction.")
- invoicing_timesheet_enabled = fields.Boolean(default=_default_invoicing_timesheet_enabled)
+ invoicing_timesheet_enabled = fields.Boolean(compute='_compute_invoicing_timesheet_enabled', store=True)
+
+ #=== COMPUTE METHODS ===#
+
+ @api.depends('sale_order_ids')
+ def _compute_invoicing_timesheet_enabled(self):
+ for wizard in self:
+ wizard.invoicing_timesheet_enabled = bool(
+ wizard.sale_order_ids.order_line.filtered(
+ lambda sol: sol.invoice_status == 'to invoice'
+ ).product_id.filtered(
+ lambda p: p._is_delivered_timesheet()
+ )
+ )
+
+ #=== BUSINESS METHODS ===#
def _create_invoices(self, sale_orders):
""" Override method from sale/wizard/sale_make_invoice_advance.py
@@ -34,7 +40,8 @@ class SaleAdvancePaymentInv(models.TransientModel):
"""
if self.advance_payment_method == 'delivered' and self.invoicing_timesheet_enabled:
if self.date_start_invoice_timesheet or self.date_end_invoice_timesheet:
- sale_orders.mapped('order_line')._recompute_qty_to_invoice(self.date_start_invoice_timesheet, self.date_end_invoice_timesheet)
+ sale_orders.order_line._recompute_qty_to_invoice(
+ self.date_start_invoice_timesheet, self.date_end_invoice_timesheet)
return sale_orders.with_context(
timesheet_start_date=self.date_start_invoice_timesheet,
diff --git a/addons/sale_timesheet/wizard/sale_make_invoice_advance_views.xml b/addons/sale_timesheet/wizard/sale_make_invoice_advance_views.xml
index 0090accb3e1..d0f2633992c 100644
--- a/addons/sale_timesheet/wizard/sale_make_invoice_advance_views.xml
+++ b/addons/sale_timesheet/wizard/sale_make_invoice_advance_views.xml
@@ -1,5 +1,6 @@
+
sale_timesheet.sale.advance.payment.inv.view.formsale.advance.payment.inv
@@ -8,7 +9,7 @@
true
-
+
@@ -22,7 +23,8 @@
options="{'related_start_date': 'date_start_invoice_timesheet'}"
title="Only timesheets not yet invoiced (and validated, if applicable) from this period will be invoiced. If the period is not indicated, all timesheets not yet invoiced (and validated, if applicable) will be invoiced without distinction."/>