2
0

[13.0][MIG] account_spread_cost_revenue

This commit is contained in:
Andrea 2020-09-07 14:31:18 +02:00 committed by Andrea Stirpe
parent 702444752b
commit 22e57f872e
25 changed files with 817 additions and 1034 deletions

View File

@ -1,22 +1,22 @@
# Copyright 2016-2019 Onestein (<https://www.onestein.eu>) # Copyright 2016-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{ {
"name": "Cost-Revenue Spread", "name": "Cost-Revenue Spread",
"summary": "Spread costs and revenues over a custom period", "summary": "Spread costs and revenues over a custom period",
"version": "12.0.1.2.0", "version": "13.0.1.0.0",
"development_status": "Beta", "development_status": "Beta",
"author": "Onestein,Odoo Community Association (OCA)", "author": "Onestein,Odoo Community Association (OCA)",
"maintainers": ["astirpe"], "maintainers": ["astirpe"],
"license": "AGPL-3", "license": "AGPL-3",
"website": "https://github.com/OCA/account-financial-tools/", "website": "https://github.com/OCA/account-financial-tools/",
"category": "Accounting & Finance", "category": "Accounting & Finance",
"depends": ["account",], "depends": ["account"],
"data": [ "data": [
"security/ir.model.access.csv", "security/ir.model.access.csv",
"security/account_spread_security.xml", "security/account_spread_security.xml",
"views/account_spread.xml", "views/account_spread.xml",
"views/account_invoice.xml", "views/account_move.xml",
"views/res_company.xml", "views/res_company.xml",
"views/account_spread_template.xml", "views/account_spread_template.xml",
"templates/assets.xml", "templates/assets.xml",

View File

@ -0,0 +1,26 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import SUPERUSER_ID, api
def migrate(cr, version):
env = api.Environment(cr, SUPERUSER_ID, {})
rule_name = "account_spread_cost_revenue.account_spread_multi_company_rule"
rule = env.ref(rule_name, raise_if_not_found=False)
if rule:
domain = "['|',('company_id','=',False),('company_id','in',company_ids)]"
rule.write({"domain_force": domain})
rule_name = "account_spread_cost_revenue.account_spread_template_multi_company_rule"
rule = env.ref(rule_name, raise_if_not_found=False)
if rule:
domain = "['|',('company_id','=',False),('company_id','in',company_ids)]"
rule.write({"domain_force": domain})
rule_name = (
"account_spread_cost_revenue.account_spread_template_auto_multi_company_rule"
)
rule = env.ref(rule_name, raise_if_not_found=False)
if rule:
domain = "['|',('company_id','=',False),('company_id','in',company_ids)]"
rule.write({"domain_force": domain})

View File

@ -0,0 +1,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tools import sql
def migrate(cr, version):
sql.rename_column(cr, "res_partner", "auto_archive", "auto_archive_spread")

View File

@ -1,7 +1,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import account_invoice from . import account_move
from . import account_invoice_line from . import account_move_line
from . import account_spread_line from . import account_spread_line
from . import account_spread from . import account_spread
from . import account_spread_template from . import account_spread_template

View File

@ -1,59 +0,0 @@
# Copyright 2016-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, models
class AccountInvoice(models.Model):
_inherit = "account.invoice"
def action_invoice_open(self):
for invoice in self:
invoice.invoice_line_ids.create_auto_spread()
return super().action_invoice_open()
@api.multi
def action_move_create(self):
"""Invoked when validating the invoices."""
res = super().action_move_create()
spreads = self.mapped("invoice_line_ids.spread_id")
spreads.compute_spread_board()
spreads.reconcile_spread_moves()
return res
@api.multi
def invoice_line_move_line_get(self):
"""Copying expense/revenue account from spread to move lines."""
res = super().invoice_line_move_line_get()
for line in res:
invl_id = line.get("invl_id")
invl = self.env["account.invoice.line"].browse(invl_id)
if invl.spread_id:
if invl.invoice_id.type in ("out_invoice", "in_refund"):
account = invl.spread_id.debit_account_id
else:
account = invl.spread_id.credit_account_id
line["account_id"] = account.id
return res
@api.multi
def action_cancel(self):
"""Cancel the spread lines and their related moves when
the invoice is canceled."""
res = super().action_cancel()
spread_lines = self.mapped("invoice_line_ids.spread_id.line_ids")
moves = spread_lines.mapped("move_id")
moves.button_cancel()
moves.unlink()
spread_lines.unlink()
return res
@api.model
def _refund_cleanup_lines(self, lines):
result = super()._refund_cleanup_lines(lines)
for i, line in enumerate(lines):
for name in line._fields.keys():
if name == "spread_id":
result[i][2][name] = False
break
return result

View File

@ -0,0 +1,34 @@
# Copyright 2016-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, models
class AccountMove(models.Model):
_inherit = "account.move"
def action_post(self):
"""Invoked when validating the invoices."""
self.mapped("invoice_line_ids").create_auto_spread()
res = super().action_post()
spreads = self.mapped("invoice_line_ids.spread_id")
spreads.compute_spread_board()
spreads.reconcile_spread_moves()
return res
def button_cancel(self):
"""Cancel the spread lines and their related moves when
the invoice is canceled."""
spread_lines = self.mapped("invoice_line_ids.spread_id.line_ids")
moves = spread_lines.mapped("move_id")
moves.line_ids.remove_move_reconcile()
moves.filtered(lambda move: move.state == "posted").button_draft()
moves.with_context(force_delete=True).unlink()
spread_lines.unlink()
res = super().button_cancel()
return res
@api.constrains("name", "journal_id", "state")
def _check_unique_sequence_number(self):
if not self.env.context.get("skip_unique_sequence_number"):
return super()._check_unique_sequence_number()

View File

@ -1,12 +1,12 @@
# Copyright 2016-2019 Onestein (<https://www.onestein.eu>) # Copyright 2016-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.exceptions import UserError from odoo.exceptions import UserError, ValidationError
class AccountInvoiceLine(models.Model): class AccountMoveLine(models.Model):
_inherit = "account.invoice.line" _inherit = "account.move.line"
spread_id = fields.Many2one("account.spread", string="Spread Board", copy=False) spread_id = fields.Many2one("account.spread", string="Spread Board", copy=False)
spread_check = fields.Selection( spread_check = fields.Selection(
@ -18,17 +18,16 @@ class AccountInvoiceLine(models.Model):
compute="_compute_spread_check", compute="_compute_spread_check",
) )
@api.depends("spread_id", "invoice_id.state") @api.depends("spread_id", "move_id.state")
def _compute_spread_check(self): def _compute_spread_check(self):
for line in self: for line in self:
if line.spread_id: if line.spread_id:
line.spread_check = "linked" line.spread_check = "linked"
elif line.invoice_id.state == "draft": elif line.move_id.state == "draft":
line.spread_check = "unlinked" line.spread_check = "unlinked"
else: else:
line.spread_check = "unavailable" line.spread_check = "unavailable"
@api.multi
def spread_details(self): def spread_details(self):
"""Button on the invoice lines tree view of the invoice """Button on the invoice lines tree view of the invoice
form to show the spread form view.""" form to show the spread form view."""
@ -39,7 +38,6 @@ class AccountInvoiceLine(models.Model):
if self.spread_id: if self.spread_id:
return { return {
"name": _("Spread Details"), "name": _("Spread Details"),
"view_type": "form",
"view_mode": "form", "view_mode": "form",
"res_model": "account.spread", "res_model": "account.spread",
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -50,16 +48,14 @@ class AccountInvoiceLine(models.Model):
# In case no spread board is linked to the invoice line # In case no spread board is linked to the invoice line
# open the wizard to link them # open the wizard to link them
company = self.invoice_id.company_id
ctx = dict( ctx = dict(
self.env.context, self.env.context,
default_invoice_line_id=self.id, default_invoice_line_id=self.id,
default_company_id=company.id, default_company_id=self.move_id.company_id.id,
allow_spread_planning=company.allow_spread_planning, allow_spread_planning=self.move_id.company_id.allow_spread_planning,
) )
return { return {
"name": _("Link Invoice Line with Spread Board"), "name": _("Link Invoice Line with Spread Board"),
"view_type": "form",
"view_mode": "form", "view_mode": "form",
"res_model": "account.spread.invoice.line.link.wizard", "res_model": "account.spread.invoice.line.link.wizard",
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -67,6 +63,52 @@ class AccountInvoiceLine(models.Model):
"context": ctx, "context": ctx,
} }
@api.constrains("spread_id", "account_id")
def _check_spread_account_balance_sheet(self):
for line in self:
if not line.spread_id:
pass
elif line.move_id.type in ("out_invoice", "in_refund"):
if line.account_id != line.spread_id.debit_account_id:
raise ValidationError(
_(
"The account of the invoice line does not correspond "
"to the Balance Sheet (debit account) of the spread"
)
)
elif line.move_id.type in ("in_invoice", "out_refund"):
if line.account_id != line.spread_id.credit_account_id:
raise ValidationError(
_(
"The account of the invoice line does not correspond "
"to the Balance Sheet (credit account) of the spread"
)
)
def write(self, vals):
if vals.get("spread_id"):
spread = self.env["account.spread"].browse(vals.get("spread_id"))
if spread.invoice_type in ["out_invoice", "in_refund"]:
vals["account_id"] = spread.debit_account_id.id
else:
vals["account_id"] = spread.credit_account_id.id
return super().write(vals)
def _check_spread_reconcile_validity(self):
# Improve error messages of standard Odoo
reconciled_lines = self.filtered(lambda l: l.reconciled)
msg_line = _("Move line: %s (%s), account code: %s\n")
if reconciled_lines:
msg = _("Cannot reconcile entries that are already reconciled:\n")
for line in reconciled_lines:
msg += msg_line % (line.id, line.name, line.account_id.code)
raise ValidationError(msg)
if len(self.mapped("account_id").ids) > 1:
msg = _("Some entries are not from the same account:\n")
for line in self:
msg += msg_line % (line.id, line.name, line.account_id.code)
raise ValidationError(msg)
def create_auto_spread(self): def create_auto_spread(self):
""" Create auto spread table for each invoice line, when needed """ """ Create auto spread table for each invoice line, when needed """
@ -88,7 +130,7 @@ class AccountInvoiceLine(models.Model):
continue continue
spread_type = ( spread_type = (
"sale" "sale"
if line.invoice_type in ["out_invoice", "out_refund"] if line.move_id.type in ["out_invoice", "out_refund"]
else "purchase" else "purchase"
) )
spread_auto = self.env["account.spread.template.auto"].search( spread_auto = self.env["account.spread.template.auto"].search(

View File

@ -1,4 +1,4 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>) # Copyright 2018-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import calendar import calendar
@ -10,8 +10,6 @@ from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_is_zero from odoo.tools import float_is_zero
from odoo.addons import decimal_precision as dp
class AccountSpread(models.Model): class AccountSpread(models.Model):
_name = "account.spread" _name = "account.spread"
@ -46,65 +44,64 @@ class AccountSpread(models.Model):
help="Period length for the entries", help="Period length for the entries",
required=True, required=True,
) )
use_invoice_line_account = fields.Boolean(string="Use invoice line's account",) use_invoice_line_account = fields.Boolean()
credit_account_id = fields.Many2one( credit_account_id = fields.Many2one(
"account.account", string="Credit Account", required=True "account.account",
compute="_compute_credit_account_id",
readonly=False,
store=True,
required=True,
) )
debit_account_id = fields.Many2one( debit_account_id = fields.Many2one(
"account.account", string="Debit Account", required=True "account.account",
compute="_compute_debit_account_id",
readonly=False,
store=True,
required=True,
) )
is_credit_account_deprecated = fields.Boolean( is_credit_account_deprecated = fields.Boolean(
compute="_compute_deprecated_accounts" compute="_compute_deprecated_accounts"
) )
is_debit_account_deprecated = fields.Boolean(compute="_compute_deprecated_accounts") is_debit_account_deprecated = fields.Boolean(compute="_compute_deprecated_accounts")
unspread_amount = fields.Float( unspread_amount = fields.Float(digits="Account", compute="_compute_amounts",)
digits=dp.get_precision("Account"), compute="_compute_amounts" unposted_amount = fields.Float(digits="Account", compute="_compute_amounts",)
) posted_amount = fields.Float(digits="Account", compute="_compute_amounts",)
unposted_amount = fields.Float( total_amount = fields.Float(digits="Account", compute="_compute_amounts",)
digits=dp.get_precision("Account"), compute="_compute_amounts" all_posted = fields.Boolean(compute="_compute_all_posted", store=True)
)
posted_amount = fields.Float(
digits=dp.get_precision("Account"), compute="_compute_amounts"
)
total_amount = fields.Float(
digits=dp.get_precision("Account"), compute="_compute_amounts"
)
all_posted = fields.Boolean(compute="_compute_amounts", store=True)
line_ids = fields.One2many( line_ids = fields.One2many(
"account.spread.line", "spread_id", string="Spread Lines" "account.spread.line", "spread_id", string="Spread Lines"
) )
spread_date = fields.Date( spread_date = fields.Date(
string="Start Date", default=time.strftime("%Y-01-01"), required=True string="Start Date", default=time.strftime("%Y-01-01"), required=True
) )
journal_id = fields.Many2one("account.journal", string="Journal", required=True) journal_id = fields.Many2one(
"account.journal",
compute="_compute_journal_id",
readonly=False,
store=True,
required=True,
)
invoice_line_ids = fields.One2many( invoice_line_ids = fields.One2many(
"account.invoice.line", "spread_id", copy=False, string="Invoice Lines" "account.move.line", "spread_id", copy=False, string="Invoice Lines"
) )
invoice_line_id = fields.Many2one( invoice_line_id = fields.Many2one(
"account.invoice.line", "account.move.line",
string="Invoice line", string="Invoice line",
compute="_compute_invoice_line", compute="_compute_invoice_line",
inverse="_inverse_invoice_line", inverse="_inverse_invoice_line",
store=True, store=True,
) )
invoice_id = fields.Many2one( invoice_id = fields.Many2one(
related="invoice_line_id.invoice_id", related="invoice_line_id.move_id", readonly=True, store=True,
readonly=True,
store=True,
string="Invoice",
) )
estimated_amount = fields.Float(digits=dp.get_precision("Account")) estimated_amount = fields.Float(digits="Account")
company_id = fields.Many2one( company_id = fields.Many2one(
"res.company", "res.company", default=lambda self: self.env.company, required=True
default=lambda self: self.env.user.company_id,
string="Company",
required=True,
) )
currency_id = fields.Many2one( currency_id = fields.Many2one(
"res.currency", "res.currency",
string="Currency",
required=True, required=True,
default=lambda self: self.env.user.company_id.currency_id.id, default=lambda self: self.env.company.currency_id.id,
) )
account_analytic_id = fields.Many2one( account_analytic_id = fields.Many2one(
"account.analytic.account", string="Analytic Account" "account.analytic.account", string="Analytic Account"
@ -112,10 +109,10 @@ class AccountSpread(models.Model):
analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags") analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags")
move_line_auto_post = fields.Boolean("Auto-post lines", default=True) move_line_auto_post = fields.Boolean("Auto-post lines", default=True)
display_create_all_moves = fields.Boolean( display_create_all_moves = fields.Boolean(
compute="_compute_display_create_all_moves", string="Display Button All Moves" compute="_compute_display_create_all_moves",
) )
display_recompute_buttons = fields.Boolean( display_recompute_buttons = fields.Boolean(
compute="_compute_display_recompute_buttons", string="Display Buttons Recompute" compute="_compute_display_recompute_buttons",
) )
display_move_line_auto_post = fields.Boolean( display_move_line_auto_post = fields.Boolean(
compute="_compute_display_move_line_auto_post", compute="_compute_display_move_line_auto_post",
@ -123,18 +120,19 @@ class AccountSpread(models.Model):
) )
active = fields.Boolean(default=True) active = fields.Boolean(default=True)
@api.model
def default_journal(self, company_id):
domain = [("type", "=", "general"), ("company_id", "=", company_id)]
return self.env["account.journal"].search(domain, limit=1)
@api.model @api.model
def default_get(self, fields): def default_get(self, fields):
res = super().default_get(fields) res = super().default_get(fields)
if "company_id" not in fields: if "journal_id" not in res:
company_id = self.env.user.company_id.id company_id = res.get("company_id", self.env.company.id)
else: default_journal = self.default_journal(company_id)
company_id = res["company_id"] if default_journal:
default_journal = self.env["account.journal"].search( res["journal_id"] = default_journal.id
[("type", "=", "general"), ("company_id", "=", company_id)], limit=1
)
if "journal_id" not in res and default_journal:
res["journal_id"] = default_journal.id
return res return res
@api.depends("invoice_type") @api.depends("invoice_type")
@ -145,20 +143,16 @@ class AccountSpread(models.Model):
else: else:
spread.spread_type = "purchase" spread.spread_type = "purchase"
@api.depends("invoice_line_ids", "invoice_line_ids.invoice_id") @api.depends("invoice_line_ids", "invoice_line_ids.move_id")
def _compute_invoice_line(self): def _compute_invoice_line(self):
for spread in self: for spread in self:
invoice_lines = spread.invoice_line_ids invoice_lines = spread.invoice_line_ids
line = invoice_lines and invoice_lines[0] or False spread.invoice_line_id = invoice_lines and invoice_lines[0] or False
spread.invoice_line_id = line
@api.multi
def _inverse_invoice_line(self): def _inverse_invoice_line(self):
for spread in self: for spread in self:
invoice_line = spread.invoice_line_id invoice_line = spread.invoice_line_id
spread.write( spread.write({"invoice_line_ids": [(6, 0, [invoice_line.id])]})
{"invoice_line_ids": [(6, 0, [invoice_line.id])],}
)
@api.depends( @api.depends(
"estimated_amount", "estimated_amount",
@ -171,38 +165,36 @@ class AccountSpread(models.Model):
) )
def _compute_amounts(self): def _compute_amounts(self):
for spread in self: for spread in self:
moves_amount = 0.0 lines_move = spread.line_ids.filtered(lambda l: l.move_id)
posted_amount = 0.0 moves_amount = sum(spread_line.amount for spread_line in lines_move)
lines_posted = lines_move.filtered(lambda l: l.move_id.state == "posted")
posted_amount = sum(spread_line.amount for spread_line in lines_posted)
total_amount = spread.estimated_amount total_amount = spread.estimated_amount
if spread.invoice_line_id: if spread.invoice_line_id:
invoice = spread.invoice_line_id.invoice_id
total_amount = spread.invoice_line_id.currency_id._convert( total_amount = spread.invoice_line_id.currency_id._convert(
spread.invoice_line_id.price_subtotal, spread.invoice_line_id.balance,
spread.currency_id, spread.currency_id,
spread.company_id, spread.company_id,
invoice._get_currency_rate_date() or fields.Date.today(), spread.invoice_id.date,
) )
for spread_line in spread.line_ids:
if spread_line.move_id:
moves_amount += spread_line.amount
if spread_line.move_id.state == "posted":
posted_amount += spread_line.amount
spread.unspread_amount = total_amount - moves_amount spread.unspread_amount = total_amount - moves_amount
spread.unposted_amount = total_amount - posted_amount spread.unposted_amount = total_amount - posted_amount
spread.posted_amount = posted_amount spread.posted_amount = posted_amount
spread.total_amount = total_amount spread.total_amount = total_amount
spread.all_posted = spread.unposted_amount == 0.0
@api.multi @api.depends("unposted_amount")
def _compute_all_posted(self):
for spread in self:
rounding = self.currency_id.rounding
unposted = spread.unposted_amount
spread.all_posted = float_is_zero(unposted, precision_rounding=rounding)
def _compute_display_create_all_moves(self): def _compute_display_create_all_moves(self):
for spread in self: for spread in self:
if any(not line.move_id for line in spread.line_ids): any_not_move = any(not line.move_id for line in spread.line_ids)
spread.display_create_all_moves = True spread.display_create_all_moves = any_not_move
else:
spread.display_create_all_moves = False
@api.multi
def _compute_display_recompute_buttons(self): def _compute_display_recompute_buttons(self):
for spread in self: for spread in self:
spread.display_recompute_buttons = True spread.display_recompute_buttons = True
@ -210,14 +202,12 @@ class AccountSpread(models.Model):
if spread.invoice_id.state == "draft": if spread.invoice_id.state == "draft":
spread.display_recompute_buttons = False spread.display_recompute_buttons = False
@api.multi @api.depends("company_id.force_move_auto_post")
def _compute_display_move_line_auto_post(self): def _compute_display_move_line_auto_post(self):
for spread in self: for spread in self:
spread.display_move_line_auto_post = True auto_post = spread.company_id.force_move_auto_post
if spread.company_id.force_move_auto_post: spread.display_move_line_auto_post = not auto_post
spread.display_move_line_auto_post = False
@api.multi
def _get_spread_entry_name(self, seq): def _get_spread_entry_name(self, seq):
"""Use this method to customise the name of the accounting entry.""" """Use this method to customise the name of the accounting entry."""
self.ensure_one() self.ensure_one()
@ -239,66 +229,67 @@ class AccountSpread(models.Model):
if self.template_id.start_date: if self.template_id.start_date:
self.spread_date = self.template_id.start_date self.spread_date = self.template_id.start_date
@api.onchange("invoice_type", "company_id") @api.depends("invoice_type", "company_id")
def onchange_invoice_type(self): def _compute_journal_id(self):
company = self.company_id
if not self.env.context.get("default_journal_id"): if not self.env.context.get("default_journal_id"):
journal = company.default_spread_expense_journal_id for spread in self:
if self.invoice_type in ("out_invoice", "in_refund"): journal = spread.company_id.default_spread_expense_journal_id
journal = company.default_spread_revenue_journal_id if spread.invoice_type in ("out_invoice", "in_refund"):
if journal: journal = spread.company_id.default_spread_revenue_journal_id
self.journal_id = journal if not journal:
journal = self.default_journal(spread.company_id.id)
spread.journal_id = journal
@api.depends("invoice_type", "company_id")
def _compute_debit_account_id(self):
if not self.env.context.get("default_debit_account_id"): if not self.env.context.get("default_debit_account_id"):
if self.invoice_type in ("out_invoice", "in_refund"): invoice_types = ("out_invoice", "in_refund")
debit_account_id = company.default_spread_revenue_account_id for spread in self.filtered(lambda s: s.invoice_type in invoice_types):
self.debit_account_id = debit_account_id debit_account = spread.company_id.default_spread_revenue_account_id
spread.debit_account_id = debit_account
@api.depends("invoice_type", "company_id")
def _compute_credit_account_id(self):
if not self.env.context.get("default_credit_account_id"): if not self.env.context.get("default_credit_account_id"):
if self.invoice_type in ("in_invoice", "out_refund"): invoice_types = ("in_invoice", "out_refund")
credit_account_id = company.default_spread_expense_account_id for spread in self.filtered(lambda s: s.invoice_type in invoice_types):
self.credit_account_id = credit_account_id credit_account = spread.company_id.default_spread_expense_account_id
spread.credit_account_id = credit_account
@api.constrains("invoice_id", "invoice_type") @api.constrains("invoice_id", "invoice_type")
def _check_invoice_type(self): def _check_invoice_type(self):
for spread in self: if self.filtered(
if not spread.invoice_id: lambda s: s.invoice_id and s.invoice_type != s.invoice_id.type
pass ):
elif spread.invoice_type != spread.invoice_id.type: raise ValidationError(
raise ValidationError( _("The Invoice Type does not correspond to the Invoice")
_("The Invoice Type does not correspond to the Invoice") )
)
@api.constrains("journal_id") @api.constrains("journal_id")
def _check_journal(self): def _check_journal(self):
for spread in self: for spread in self:
moves = spread.mapped("line_ids.move_id").filtered("journal_id") moves = spread.mapped("line_ids.move_id").filtered("journal_id")
if any(move.journal_id != spread.journal_id for move in moves): if any(move.journal_id != spread.journal_id for move in moves):
raise ValidationError( err_msg = _("The Journal is not consistent with the account moves.")
_("The Journal is not consistent with the account moves.") raise ValidationError(err_msg)
)
@api.constrains("template_id", "invoice_type") @api.constrains("template_id", "invoice_type")
def _check_template_invoice_type(self): def _check_template_invoice_type(self):
for spread in self: for spread in self.filtered(lambda s: s.template_id.spread_type == "sale"):
if spread.invoice_type in ["in_invoice", "in_refund"]: if spread.invoice_type in ["in_invoice", "in_refund"]:
if spread.template_id.spread_type == "sale": err_msg = _(
raise ValidationError( "The Spread Template (Sales) is not compatible "
_( "with selected invoice type"
"The Spread Template (Sales) is not compatible " )
"with selected invoice type" raise ValidationError(err_msg)
) for spread in self.filtered(lambda s: s.template_id.spread_type == "purchase"):
) if spread.invoice_type in ["out_invoice", "out_refund"]:
elif spread.invoice_type in ["out_invoice", "out_refund"]: err_msg = _(
if spread.template_id.spread_type == "purchase": "The Spread Template (Purchases) is not compatible "
raise ValidationError( "with selected invoice type"
_( )
"The Spread Template (Purchases) is not compatible " raise ValidationError(err_msg)
"with selected invoice type"
)
)
@api.multi
def _get_spread_period_duration(self): def _get_spread_period_duration(self):
"""Converts the selected period_type to number of months.""" """Converts the selected period_type to number of months."""
self.ensure_one() self.ensure_one()
@ -308,7 +299,6 @@ class AccountSpread(models.Model):
return 3 return 3
return 1 return 1
@api.multi
def _init_line_date(self, posted_line_ids): def _init_line_date(self, posted_line_ids):
"""Calculates the initial spread date. This method """Calculates the initial spread date. This method
is used by "def _compute_spread_board()" method. is used by "def _compute_spread_board()" method.
@ -324,7 +314,6 @@ class AccountSpread(models.Model):
spread_date = self.spread_date spread_date = self.spread_date
return spread_date return spread_date
@api.multi
def _next_line_date(self, month_day, date): def _next_line_date(self, month_day, date):
"""Calculates the next spread date. This method """Calculates the next spread date. This method
is used by "def _compute_spread_board()" method. is used by "def _compute_spread_board()" method.
@ -338,11 +327,10 @@ class AccountSpread(models.Model):
date = date.replace(day=min(max_day_in_month, month_day)) date = date.replace(day=min(max_day_in_month, month_day))
return date return date
@api.multi
def _compute_spread_board(self): def _compute_spread_board(self):
"""Creates the spread lines. This method is highly inspired """Creates the spread lines. This method is highly inspired
from method compute_depreciation_board() present in standard from method compute_depreciation_board() present in standard
"account_asset" module, developed by Odoo SA. Odoo 11.0 "account_asset" module, developed by Odoo SA.
""" """
self.ensure_one() self.ensure_one()
@ -391,19 +379,15 @@ class AccountSpread(models.Model):
msg_body = _("Spread table '%s' created.") % invoice_type_selection msg_body = _("Spread table '%s' created.") % invoice_type_selection
self.message_post(body=msg_body) self.message_post(body=msg_body)
@api.multi
def _get_number_of_periods(self, month_day): def _get_number_of_periods(self, month_day):
"""Calculates the number of spread lines.""" """Calculates the number of spread lines."""
self.ensure_one() self.ensure_one()
if month_day != 1: return self.period_number + 1 if month_day != 1 else self.period_number
return self.period_number + 1
return self.period_number
@staticmethod @staticmethod
def _get_last_day_of_month(spread_date): def _get_last_day_of_month(spread_date):
return spread_date + relativedelta(day=31) return spread_date + relativedelta(day=31)
@api.multi
def _compute_board_amount(self, sequence, amount, number_of_periods): def _compute_board_amount(self, sequence, amount, number_of_periods):
"""Calculates the amount for the spread lines.""" """Calculates the amount for the spread lines."""
self.ensure_one() self.ensure_one()
@ -418,7 +402,6 @@ class AccountSpread(models.Model):
amount = (amount_to_spread / period) / month_days * days amount = (amount_to_spread / period) / month_days * days
return amount return amount
@api.multi
def compute_spread_board(self): def compute_spread_board(self):
"""Checks whether the spread lines should be calculated. """Checks whether the spread lines should be calculated.
In case checks pass, invoke "def _compute_spread_board()" method. In case checks pass, invoke "def _compute_spread_board()" method.
@ -426,7 +409,6 @@ class AccountSpread(models.Model):
for spread in self.filtered(lambda s: s.total_amount): for spread in self.filtered(lambda s: s.total_amount):
spread._compute_spread_board() spread._compute_spread_board()
@api.multi
def action_recalculate_spread(self): def action_recalculate_spread(self):
"""Recalculate spread""" """Recalculate spread"""
self.ensure_one() self.ensure_one()
@ -435,37 +417,30 @@ class AccountSpread(models.Model):
self.compute_spread_board() self.compute_spread_board()
self.env["account.spread.line"]._create_entries() self.env["account.spread.line"]._create_entries()
@api.multi
def action_undo_spread(self): def action_undo_spread(self):
"""Undo spreading: Remove all created moves, """Undo spreading: Remove all created moves"""
restore original account on move line"""
self.ensure_one() self.ensure_one()
self.mapped("line_ids").filtered("move_id").unlink_move() self.mapped("line_ids").filtered("move_id").unlink_move()
self.mapped("line_ids").unlink() self.mapped("line_ids").unlink()
@api.multi
def action_unlink_invoice_line(self): def action_unlink_invoice_line(self):
"""Unlink the invoice line from the spread board""" """Unlink the invoice line from the spread board"""
self.ensure_one() self.ensure_one()
if self.invoice_id.state != "draft": if self.invoice_id.state != "draft":
raise UserError( msg = _("Cannot unlink invoice lines if the invoice is validated")
_("Cannot unlink invoice lines if the invoice is validated") raise UserError(msg)
)
self._action_unlink_invoice_line() self._action_unlink_invoice_line()
@api.multi
def _action_unlink_invoice_line(self): def _action_unlink_invoice_line(self):
spread_mls = self.mapped("line_ids.move_id.line_ids") self.mapped("line_ids.move_id.line_ids").remove_move_reconcile()
spread_mls.remove_move_reconcile()
self._message_post_unlink_invoice_line() self._message_post_unlink_invoice_line()
self.write({"invoice_line_ids": [(5, 0, 0)]}) self.write({"invoice_line_ids": [(5, 0, 0)]})
def _message_post_unlink_invoice_line(self): def _message_post_unlink_invoice_line(self):
for spread in self: for spread in self:
invoice_id = spread.invoice_id.id
inv_link = ( inv_link = (
"<a href=# data-oe-model=account.invoice " "<a href=# data-oe-model=account.move "
"data-oe-id=%d>%s</a>" % (invoice_id, _("Invoice")) "data-oe-id=%d>%s</a>" % (spread.invoice_id.id, _("Invoice"))
) )
msg_body = _("Unlinked invoice line '%s' (view %s).") % ( msg_body = _("Unlinked invoice line '%s' (view %s).") % (
spread.invoice_line_id.name, spread.invoice_line_id.name,
@ -482,91 +457,61 @@ class AccountSpread(models.Model):
) )
spread.invoice_id.message_post(body=msg_body) spread.invoice_id.message_post(body=msg_body)
@api.multi
def unlink(self): def unlink(self):
if self.filtered(lambda s: s.invoice_line_id): if self.filtered(lambda s: s.invoice_line_id):
raise UserError( err_msg = _("Cannot delete spread(s) that are linked to an invoice line.")
_("Cannot delete spread(s) that are linked " "to an invoice line.") raise UserError(err_msg)
)
if self.mapped("line_ids.move_id").filtered(lambda m: m.state == "posted"): if self.mapped("line_ids.move_id").filtered(lambda m: m.state == "posted"):
raise ValidationError( err_msg = _("Cannot delete spread(s): there are posted Journal Entries.")
_("Cannot delete spread(s): there are " "posted Journal Entries.") raise ValidationError(err_msg)
)
return super().unlink() return super().unlink()
@api.multi
def reconcile_spread_moves(self): def reconcile_spread_moves(self):
for spread in self: for spread in self:
spread._reconcile_spread_moves() spread._reconcile_spread_moves()
@api.multi
def _reconcile_spread_moves(self, created_moves=False): def _reconcile_spread_moves(self, created_moves=False):
"""Reconcile spread moves if possible""" """Reconcile spread moves if possible"""
self.ensure_one() self.ensure_one()
if not self.invoice_id.number:
return
spread_mls = self.line_ids.mapped("move_id.line_ids") spread_mls = self.line_ids.mapped("move_id.line_ids")
if created_moves: if created_moves:
spread_mls |= created_moves.mapped("line_ids") spread_mls |= created_moves.mapped("line_ids")
spread_sign = True if self.total_amount >= 0.0 else False account = self.invoice_line_id.account_id
in_invoice_or_out_refund = ("in_invoice", "out_refund") mls_to_reconcile = spread_mls.filtered(lambda l: l.account_id == account)
if self.invoice_type in in_invoice_or_out_refund and spread_sign: if mls_to_reconcile:
spread_mls = spread_mls.filtered(lambda x: x.credit != 0.0) do_reconcile = mls_to_reconcile + self.invoice_line_id
elif self.invoice_type in in_invoice_or_out_refund:
spread_mls = spread_mls.filtered(lambda x: x.debit != 0.0)
elif spread_sign:
spread_mls = spread_mls.filtered(lambda x: x.debit != 0.0)
else:
spread_mls = spread_mls.filtered(lambda x: x.credit != 0.0)
invoice_mls = self.invoice_id.move_id.mapped("line_ids")
if self.invoice_id.type in in_invoice_or_out_refund and spread_sign:
invoice_mls = invoice_mls.filtered(lambda x: x.debit != 0.0)
elif self.invoice_id.type in in_invoice_or_out_refund:
invoice_mls = invoice_mls.filtered(lambda x: x.credit != 0.0)
elif spread_sign:
invoice_mls = invoice_mls.filtered(lambda x: x.credit != 0.0)
else:
invoice_mls = invoice_mls.filtered(lambda x: x.debit != 0.0)
to_be_reconciled = self.env["account.move.line"]
if len(invoice_mls) > 1:
# Refine selection of move line.
# The name is formatted the same way as it is done when creating
# move lines in method "def invoice_line_move_line_get()" of
# standard account module
raw_name = self.invoice_line_id.name
formatted_name = raw_name.split("\n")[0][:64]
for move_line in invoice_mls:
if move_line.name == formatted_name:
to_be_reconciled |= move_line
else:
to_be_reconciled = invoice_mls
if len(to_be_reconciled) == 1:
do_reconcile = spread_mls + to_be_reconciled
do_reconcile.remove_move_reconcile() do_reconcile.remove_move_reconcile()
do_reconcile._check_spread_reconcile_validity()
do_reconcile.reconcile() do_reconcile.reconcile()
@api.multi
def create_all_moves(self): def create_all_moves(self):
for line in self.mapped("line_ids").filtered(lambda l: not l.move_id): for line in self.mapped("line_ids").filtered(lambda l: not l.move_id):
line.create_move() line.create_move()
def _post_spread_moves(self, moves):
self.ensure_one()
if not moves:
return
ctx = dict(self.env.context, skip_unique_sequence_number=True)
if self.company_id.force_move_auto_post or self.move_line_auto_post:
moves.with_context(ctx).post()
@api.depends("debit_account_id.deprecated", "credit_account_id.deprecated") @api.depends("debit_account_id.deprecated", "credit_account_id.deprecated")
def _compute_deprecated_accounts(self): def _compute_deprecated_accounts(self):
for spread in self: for spread in self:
debit_deprecated = bool(spread.debit_account_id.deprecated) spread.is_debit_account_deprecated = spread.debit_account_id.deprecated
credit_deprecated = bool(spread.credit_account_id.deprecated) spread.is_credit_account_deprecated = spread.credit_account_id.deprecated
spread.is_debit_account_deprecated = debit_deprecated
spread.is_credit_account_deprecated = credit_deprecated
@api.multi
def open_reconcile_view(self): def open_reconcile_view(self):
self.ensure_one() action_name = "account_spread_cost_revenue.action_account_moves_all_spread"
[action] = self.env.ref(action_name).read()
action["domain"] = [("id", "in", [])]
spread_mls = self.line_ids.mapped("move_id.line_ids") spread_mls = self.line_ids.mapped("move_id.line_ids")
return spread_mls.open_reconcile_view() spread_mls = spread_mls.filtered(lambda m: m.reconciled)
if spread_mls:
domain = [("id", "in", spread_mls.ids + [self.invoice_line_id.id])]
action["domain"] = domain
return action

View File

@ -1,11 +1,9 @@
# Copyright 2016-2019 Onestein (<https://www.onestein.eu>) # Copyright 2016-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.addons import decimal_precision as dp
class AccountInvoiceSpreadLine(models.Model): class AccountInvoiceSpreadLine(models.Model):
_name = "account.spread.line" _name = "account.spread.line"
@ -13,14 +11,11 @@ class AccountInvoiceSpreadLine(models.Model):
_order = "date" _order = "date"
name = fields.Char("Description", readonly=True) name = fields.Char("Description", readonly=True)
amount = fields.Float(digits=dp.get_precision("Account"), required=True) amount = fields.Float(digits="Account", required=True)
date = fields.Date(required=True) date = fields.Date(required=True)
spread_id = fields.Many2one( spread_id = fields.Many2one("account.spread", ondelete="cascade")
"account.spread", string="Spread Board", ondelete="cascade"
)
move_id = fields.Many2one("account.move", string="Journal Entry", readonly=True) move_id = fields.Many2one("account.move", string="Journal Entry", readonly=True)
@api.multi
def create_and_reconcile_moves(self): def create_and_reconcile_moves(self):
grouped_lines = {} grouped_lines = {}
for spread_line in self: for spread_line in self:
@ -40,27 +35,15 @@ class AccountInvoiceSpreadLine(models.Model):
for move in created_moves for move in created_moves
) )
spread.message_post(body=post_msg) spread.message_post(body=post_msg)
if spread.invoice_id.state == "posted":
spread._reconcile_spread_moves(created_moves)
spread._post_spread_moves(created_moves)
spread._reconcile_spread_moves(created_moves)
self._post_spread_moves(spread, created_moves)
@api.model
def _post_spread_moves(self, spread, moves):
if not moves:
return
if spread.company_id.force_move_auto_post:
moves.post()
elif spread.move_line_auto_post:
moves.post()
@api.multi
def create_move(self): def create_move(self):
"""Button to manually create a move from a spread line entry. """Button to manually create a move from a spread line entry."""
"""
self.ensure_one() self.ensure_one()
self.create_and_reconcile_moves() self.create_and_reconcile_moves()
@api.multi
def _create_moves(self): def _create_moves(self):
if self.filtered(lambda l: l.move_id): if self.filtered(lambda l: l.move_id):
raise UserError( raise UserError(
@ -74,19 +57,21 @@ class AccountInvoiceSpreadLine(models.Model):
for line in self: for line in self:
move_vals = line._prepare_move() move_vals = line._prepare_move()
move = self.env["account.move"].create(move_vals) move = self.env["account.move"].create(move_vals)
line.move_id = move
line.write({"move_id": move.id})
created_moves += move created_moves += move
return created_moves return created_moves
@api.multi
def _prepare_move(self): def _prepare_move(self):
self.ensure_one() self.ensure_one()
spread_date = self.env.context.get("spread_date") or self.date spread_date = self.env.context.get("spread_date") or self.date
spread = self.spread_id spread = self.spread_id
analytic = spread.account_analytic_id analytic = spread.account_analytic_id
analytic_tags = [(4, tag.id, None) for tag in spread.analytic_tag_ids] analytic_tags = []
if self.env["account.analytic.tag"].check_access_rights(
"read", raise_exception=False
):
analytic_tags = [(6, 0, spread.analytic_tag_ids.ids)]
company_currency = spread.company_id.currency_id company_currency = spread.company_id.currency_id
current_currency = spread.currency_id current_currency = spread.currency_id
@ -95,13 +80,17 @@ class AccountInvoiceSpreadLine(models.Model):
self.amount, company_currency, spread.company_id, spread_date self.amount, company_currency, spread.company_id, spread_date
) )
debit_credit = spread.invoice_type in ["in_invoice", "out_refund"]
line_ids = [ line_ids = [
( (
0, 0,
0, 0,
{ {
"name": spread.name.split("\n")[0][:64], "name": spread.name,
"account_id": spread.debit_account_id.id, "account_id": spread.debit_account_id.id
if debit_credit
else spread.credit_account_id.id,
"debit": amount if amount > 0.0 else 0.0, "debit": amount if amount > 0.0 else 0.0,
"credit": -amount if amount < 0.0 else 0.0, "credit": -amount if amount < 0.0 else 0.0,
"partner_id": self.spread_id.invoice_id.partner_id.id, "partner_id": self.spread_id.invoice_id.partner_id.id,
@ -115,8 +104,10 @@ class AccountInvoiceSpreadLine(models.Model):
0, 0,
0, 0,
{ {
"name": spread.name.split("\n")[0][:64], "name": spread.name,
"account_id": spread.credit_account_id.id, "account_id": spread.credit_account_id.id
if debit_credit
else spread.debit_account_id.id,
"credit": amount if amount > 0.0 else 0.0, "credit": amount if amount > 0.0 else 0.0,
"debit": -amount if amount < 0.0 else 0.0, "debit": -amount if amount < 0.0 else 0.0,
"partner_id": self.spread_id.invoice_id.partner_id.id, "partner_id": self.spread_id.invoice_id.partner_id.id,
@ -135,17 +126,14 @@ class AccountInvoiceSpreadLine(models.Model):
"journal_id": spread.journal_id.id, "journal_id": spread.journal_id.id,
"line_ids": line_ids, "line_ids": line_ids,
"company_id": spread.company_id.id, "company_id": spread.company_id.id,
"partner_id": spread.invoice_id.partner_id.id,
} }
@api.multi
def open_move(self): def open_move(self):
"""Used by a button to manually view a move from a """Used by a button to manually view a move from a spread line entry."""
spread line entry.
"""
self.ensure_one() self.ensure_one()
return { return {
"name": _("Journal Entry"), "name": _("Journal Entry"),
"view_type": "form",
"view_mode": "form", "view_mode": "form",
"res_model": "account.move", "res_model": "account.move",
"view_id": False, "view_id": False,
@ -153,18 +141,15 @@ class AccountInvoiceSpreadLine(models.Model):
"res_id": self.move_id.id, "res_id": self.move_id.id,
} }
@api.multi
def unlink_move(self): def unlink_move(self):
"""Used by a button to manually unlink a move """Used by a button to manually unlink a move from a spread line entry."""
from a spread line entry.
"""
for line in self: for line in self:
move = line.move_id move = line.move_id
if move.state == "posted": if move.state == "posted":
move.button_cancel() move.button_cancel()
move.line_ids.remove_move_reconcile() move.line_ids.remove_move_reconcile()
post_msg = _("Deleted move %s") % line.move_id.id post_msg = _("Deleted move %s") % line.move_id.id
move.unlink() move.with_context(force_delete=True).unlink()
line.move_id = False line.move_id = False
line.spread_id.message_post(body=post_msg) line.spread_id.message_post(body=post_msg)
@ -188,6 +173,6 @@ class AccountInvoiceSpreadLine(models.Model):
spreads_to_archive = ( spreads_to_archive = (
self.env["account.spread"] self.env["account.spread"]
.search([("all_posted", "=", True)]) .search([("all_posted", "=", True)])
.filtered(lambda s: s.company_id.auto_archive) .filtered(lambda s: s.company_id.auto_archive_spread)
) )
spreads_to_archive.write({"active": False}) spreads_to_archive.write({"active": False})

View File

@ -1,4 +1,4 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>) # Copyright 2018-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models from odoo import _, api, fields, models
@ -14,13 +14,15 @@ class AccountSpreadTemplate(models.Model):
[("sale", "Customer"), ("purchase", "Supplier")], default="sale", required=True [("sale", "Customer"), ("purchase", "Supplier")], default="sale", required=True
) )
company_id = fields.Many2one( company_id = fields.Many2one(
"res.company", "res.company", default=lambda self: self.env.company, required=True
default=lambda self: self.env.user.company_id,
string="Company",
required=True,
) )
spread_journal_id = fields.Many2one( spread_journal_id = fields.Many2one(
"account.journal", string="Journal", required=True "account.journal",
string="Journal",
compute="_compute_spread_journal",
readonly=False,
store=True,
required=True,
) )
use_invoice_line_account = fields.Boolean( use_invoice_line_account = fields.Boolean(
string="Invoice account as spread account", string="Invoice account as spread account",
@ -28,12 +30,17 @@ class AccountSpreadTemplate(models.Model):
"In this case, user need to select expense/revenue account too.", "In this case, user need to select expense/revenue account too.",
) )
spread_account_id = fields.Many2one( spread_account_id = fields.Many2one(
"account.account", string="Spread Balance Sheet Account", required=False "account.account",
string="Spread Balance Sheet Account",
compute="_compute_spread_account",
readonly=False,
store=True,
required=False,
) )
exp_rev_account_id = fields.Many2one( exp_rev_account_id = fields.Many2one(
"account.account", "account.account",
string="Expense/Revenue Account", string="Expense/Revenue Account",
help="Optional account to overwrite the existing expense/revenue " "account", help="Optional account to overwrite the existing expense/revenue account",
) )
period_number = fields.Integer( period_number = fields.Integer(
string="Number of Repetitions", help="Define the number of spread lines" string="Number of Repetitions", help="Define the number of spread lines"
@ -57,15 +64,14 @@ class AccountSpreadTemplate(models.Model):
@api.model @api.model
def default_get(self, fields): def default_get(self, fields):
res = super().default_get(fields) res = super().default_get(fields)
if "company_id" not in fields: if not res.get("company_id"):
company_id = self.env.user.company_id.id res["company_id"] = self.env.company.id
else: if "spread_journal_id" not in res:
company_id = res["company_id"] default_journal = self.env["account.spread"].default_journal(
default_journal = self.env["account.journal"].search( res["company_id"]
[("type", "=", "general"), ("company_id", "=", company_id)], limit=1 )
) if default_journal:
if "spread_journal_id" not in res and default_journal: res["spread_journal_id"] = default_journal.id
res["spread_journal_id"] = default_journal.id
return res return res
@api.constrains("auto_spread", "auto_spread_ids") @api.constrains("auto_spread", "auto_spread_ids")
@ -80,19 +86,27 @@ class AccountSpreadTemplate(models.Model):
) )
) )
@api.onchange("spread_type", "company_id") @api.depends("spread_type", "company_id")
def onchange_spread_type(self): def _compute_spread_journal(self):
company = self.company_id for spread in self:
if self.spread_type == "sale": company = spread.company_id
account = company.default_spread_revenue_account_id if spread.spread_type == "sale":
journal = company.default_spread_revenue_journal_id journal = company.default_spread_revenue_journal_id
else: else:
account = company.default_spread_expense_account_id journal = company.default_spread_expense_journal_id
journal = company.default_spread_expense_journal_id if journal:
if account: spread.spread_journal_id = journal
self.spread_account_id = account
if journal: @api.depends("spread_type", "company_id")
self.spread_journal_id = journal def _compute_spread_account(self):
for spread in self:
company = spread.company_id
if spread.spread_type == "sale":
account = company.default_spread_revenue_account_id
else:
account = company.default_spread_expense_account_id
if account:
spread.spread_account_id = account
@api.onchange("use_invoice_line_account") @api.onchange("use_invoice_line_account")
def _onchange_user_invoice_line_account(self): def _onchange_user_invoice_line_account(self):
@ -144,7 +158,9 @@ class AccountSpreadTemplate(models.Model):
product = self.env["product.product"].browse(res[0]) product = self.env["product.product"].browse(res[0])
account = self.env["account.account"].browse(res[1]) account = self.env["account.account"].browse(res[1])
analytic = self.env["account.analytic.account"].browse(res[2]) analytic = self.env["account.analytic.account"].browse(res[2])
results.append("{} / {} / {}".format(product.name, account.name, analytic.name)) results.append(
"{} / {} / {}".format(product.name, account.name, analytic.name)
)
if results: if results:
raise UserError( raise UserError(
_("Followings are duplicated combinations,\n\n%s" % "\n".join(results)) _("Followings are duplicated combinations,\n\n%s" % "\n".join(results))

View File

@ -1,4 +1,4 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>) # Copyright 2018-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models from odoo import fields, models
@ -33,7 +33,7 @@ class ResCompany(models.Model):
help="Enable this option if you want to post automatically the " help="Enable this option if you want to post automatically the "
"accounting moves of all the spreads.", "accounting moves of all the spreads.",
) )
auto_archive = fields.Boolean( auto_archive_spread = fields.Boolean(
"Auto-archive spread", "Auto-archive spread",
help="Enable this option if you want the cron job to automatically " help="Enable this option if you want the cron job to automatically "
"archive the spreads when all lines are posted.", "archive the spreads when all lines are posted.",

View File

@ -1,3 +1,8 @@
13.0.1.0.0
~~~~~~~~~~
* [MIG] Port account_spread_cost_revenue to V13.
12.0.2.0.0 12.0.2.0.0
~~~~~~~~~~ ~~~~~~~~~~

View File

@ -7,7 +7,7 @@
<field eval="True" name="global" /> <field eval="True" name="global" />
<field <field
name="domain_force" name="domain_force"
>['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> >['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
</record> </record>
<record id="account_spread_template_multi_company_rule" model="ir.rule"> <record id="account_spread_template_multi_company_rule" model="ir.rule">
<field name="name">Account Spread Template multi-company</field> <field name="name">Account Spread Template multi-company</field>
@ -15,7 +15,7 @@
<field eval="True" name="global" /> <field eval="True" name="global" />
<field <field
name="domain_force" name="domain_force"
>['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> >['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
</record> </record>
<record id="account_spread_template_auto_multi_company_rule" model="ir.rule"> <record id="account_spread_template_auto_multi_company_rule" model="ir.rule">
<field name="name">Account Spread Tempalte Auto multi-company</field> <field name="name">Account Spread Tempalte Auto multi-company</field>
@ -23,7 +23,7 @@
<field eval="True" name="global" /> <field eval="True" name="global" />
<field <field
name="domain_force" name="domain_force"
>['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> >['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -11,7 +11,7 @@ odoo.define("account_spread_cost_revenue.widget", function(require) {
events: _.extend({}, AbstractField.prototype.events, { events: _.extend({}, AbstractField.prototype.events, {
click: "_onClick", click: "_onClick",
}), }),
description: "", noLabel: true,
/** /**
* @override * @override

View File

@ -2,110 +2,13 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.modules.module import get_resource_path
from odoo.tests import common from odoo.addons.account_spread_cost_revenue.tests.test_account_invoice_spread import (
from odoo.tools import convert_file TestAccountInvoiceSpread,
)
class TestAccountInvoiceSpread(common.TransactionCase): class TestAccountInvoiceAutoSpread(TestAccountInvoiceSpread):
def _load(self, module, *args):
convert_file(
self.cr,
"account_spread_cost_revenue",
get_resource_path(module, *args),
{},
"init",
False,
"test",
self.registry._assertion_report,
)
def setUp(self):
super().setUp()
self._load("account", "test", "account_minimal_test.xml")
type_receivable = self.env.ref("account.data_account_type_receivable")
type_payable = self.env.ref("account.data_account_type_payable")
type_revenue = self.env.ref("account.data_account_type_revenue")
self.invoice_account = self.env["account.account"].create(
{
"name": "test_account_receivable",
"code": "123",
"user_type_id": type_receivable.id,
"reconcile": True,
}
)
self.account_payable = self.env["account.account"].create(
{
"name": "test_account_payable",
"code": "321",
"user_type_id": type_payable.id,
"reconcile": True,
}
)
self.account_revenue = self.env["account.account"].create(
{
"name": "test_account_revenue",
"code": "864",
"user_type_id": type_revenue.id,
"reconcile": True,
}
)
self.invoice_line_account = self.account_payable
self.spread_account = self.env["account.account"].create(
{
"name": "test spread account_payable",
"code": "765",
"user_type_id": type_payable.id,
"reconcile": True,
}
)
partner = self.env["res.partner"].create(
{"name": "Partner Name", "supplier": True,}
)
# Purchase Invoice
self.invoice = self.env["account.invoice"].create(
{
"partner_id": partner.id,
"account_id": self.invoice_account.id,
"type": "in_invoice",
}
)
self.invoice_line = self.env["account.invoice.line"].create(
{
"quantity": 1.0,
"price_unit": 1000.0,
"invoice_id": self.invoice.id,
"name": "product that cost 1000",
"account_id": self.invoice_account.id,
}
)
# Sales Invoice
self.invoice_2 = self.env["account.invoice"].create(
{
"partner_id": partner.id,
"account_id": self.invoice_account.id,
"type": "out_invoice",
}
)
self.invoice_line_2 = self.env["account.invoice.line"].create(
{
"quantity": 1.0,
"price_unit": 1000.0,
"invoice_id": self.invoice_2.id,
"name": "product that cost 1000",
"account_id": self.invoice_line_account.id,
}
)
def test_01_no_auto_spread_sheet(self): def test_01_no_auto_spread_sheet(self):
self.env["account.spread.template"].create( self.env["account.spread.template"].create(
@ -119,13 +22,15 @@ class TestAccountInvoiceSpread(common.TransactionCase):
"account_spread_cost_revenue.expenses_journal" "account_spread_cost_revenue.expenses_journal"
), ),
"auto_spread": False, # Auto Spread = False "auto_spread": False, # Auto Spread = False
"auto_spread_ids": [(0, 0, {"account_id": self.invoice_account.id})], "auto_spread_ids": [
(0, 0, {"account_id": self.vendor_bill_line.account_id.id})
],
} }
) )
self.assertFalse(self.invoice_line.spread_id) self.assertFalse(self.vendor_bill_line.spread_id)
self.invoice.action_invoice_open() self.vendor_bill.action_post()
self.assertFalse(self.invoice_line.spread_id) self.assertFalse(self.vendor_bill_line.spread_id)
def test_02_new_auto_spread_sheet_purchase(self): def test_02_new_auto_spread_sheet_purchase(self):
@ -140,7 +45,9 @@ class TestAccountInvoiceSpread(common.TransactionCase):
"account_spread_cost_revenue.expenses_journal" "account_spread_cost_revenue.expenses_journal"
), ),
"auto_spread": True, # Auto Spread "auto_spread": True, # Auto Spread
"auto_spread_ids": [(0, 0, {"account_id": self.invoice_account.id})], "auto_spread_ids": [
(0, 0, {"account_id": self.vendor_bill_line.account_id.id})
],
} }
) )
template2 = self.env["account.spread.template"].create( template2 = self.env["account.spread.template"].create(
@ -154,20 +61,22 @@ class TestAccountInvoiceSpread(common.TransactionCase):
"account_spread_cost_revenue.expenses_journal" "account_spread_cost_revenue.expenses_journal"
), ),
"auto_spread": True, # Auto Spread "auto_spread": True, # Auto Spread
"auto_spread_ids": [(0, 0, {"account_id": self.invoice_account.id})], "auto_spread_ids": [
(0, 0, {"account_id": self.vendor_bill_line.account_id.id})
],
} }
) )
template2._check_auto_spread_ids_unique() template2._check_auto_spread_ids_unique()
self.assertFalse(self.invoice_line.spread_id) self.assertFalse(self.vendor_bill_line.spread_id)
with self.assertRaises(UserError): # too many auto_spread_ids matched with self.assertRaises(UserError): # too many auto_spread_ids matched
self.invoice.action_invoice_open() self.vendor_bill.action_post()
template2.auto_spread = False # Do not use this template template2.auto_spread = False # Do not use this template
self.invoice.action_invoice_open() self.vendor_bill.action_post()
self.assertTrue(self.invoice_line.spread_id) self.assertTrue(self.vendor_bill_line.spread_id)
spread_lines = self.invoice_line.spread_id.line_ids spread_lines = self.vendor_bill_line.spread_id.line_ids
self.assertTrue(spread_lines) self.assertTrue(spread_lines)
for line in spread_lines: for line in spread_lines:
@ -182,22 +91,22 @@ class TestAccountInvoiceSpread(common.TransactionCase):
"spread_type": "sale", "spread_type": "sale",
"period_number": 5, "period_number": 5,
"period_type": "month", "period_type": "month",
"spread_account_id": self.account_revenue.id, "spread_account_id": self.account_receivable.id,
"spread_journal_id": self.ref( "spread_journal_id": self.ref(
"account_spread_cost_revenue.sales_journal" "account_spread_cost_revenue.sales_journal"
), ),
"auto_spread": True, # Auto Spread "auto_spread": True, # Auto Spread
"auto_spread_ids": [ "auto_spread_ids": [
(0, 0, {"account_id": self.invoice_line_account.id}) (0, 0, {"account_id": self.invoice_line.account_id.id})
], ],
} }
) )
self.assertFalse(self.invoice_line_2.spread_id) self.assertFalse(self.invoice_line.spread_id)
self.invoice_2.action_invoice_open() self.sale_invoice.action_post()
self.assertTrue(self.invoice_line_2.spread_id) self.assertTrue(self.invoice_line.spread_id)
spread_lines = self.invoice_line_2.spread_id.line_ids spread_lines = self.invoice_line.spread_id.line_ids
self.assertTrue(spread_lines) self.assertTrue(spread_lines)
for line in spread_lines: for line in spread_lines:

View File

@ -1,11 +1,12 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>) # Copyright 2018-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import datetime import datetime
from odoo import fields
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.modules.module import get_resource_path from odoo.modules.module import get_resource_path
from odoo.tests import common from odoo.tests import Form, common
from odoo.tools import convert_file from odoo.tools import convert_file
@ -22,75 +23,83 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.registry._assertion_report, self.registry._assertion_report,
) )
def create_account_invoice(self, invoice_type, quantity=1.0, price_unit=1000.0):
""" Create an invoice as in a view by triggering its onchange methods"""
invoice_form = Form(
self.env["account.move"].with_context(default_type=invoice_type)
)
invoice_form.partner_id = self.env["res.partner"].create(
{"name": "Partner Name"}
)
with invoice_form.invoice_line_ids.new() as line:
line.name = "product that costs " + str(price_unit)
line.quantity = quantity
line.price_unit = price_unit
return invoice_form.save()
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self._load("account", "test", "account_minimal_test.xml") self._load("account", "test", "account_minimal_test.xml")
type_receivable = self.env.ref("account.data_account_type_receivable")
type_payable = self.env.ref("account.data_account_type_payable")
type_revenue = self.env.ref("account.data_account_type_revenue")
self.invoice_account = self.env["account.account"].create(
{
"name": "test_account_receivable",
"code": "123",
"user_type_id": type_receivable.id,
"reconcile": True,
}
)
self.account_payable = self.env["account.account"].create( self.account_payable = self.env["account.account"].create(
{ {
"name": "test_account_payable", "name": "Test account payable",
"code": "321", "code": "321spread",
"user_type_id": type_payable.id, "user_type_id": self.env.ref(
"account.data_account_type_other_income"
).id,
"reconcile": True, "reconcile": True,
} }
) )
self.account_revenue = self.env["account.account"].create( self.account_receivable = self.env["account.account"].create(
{ {
"name": "test_account_revenue", "name": "Test account receivable",
"code": "864", "code": "322spread",
"user_type_id": type_revenue.id, "user_type_id": self.env.ref(
"account.data_account_type_other_income"
).id,
"reconcile": True, "reconcile": True,
} }
) )
self.invoice_line_account = self.account_payable spread_account_payable = self.env["account.account"].create(
self.spread_account = self.env["account.account"].create(
{ {
"name": "test spread account_payable", "name": "test spread account_payable",
"code": "765", "code": "765spread",
"user_type_id": type_payable.id, "user_type_id": self.env.ref(
"account.data_account_type_other_income"
).id,
"reconcile": True, "reconcile": True,
} }
) )
partner = self.env["res.partner"].create( spread_account_receivable = self.env["account.account"].create(
{"name": "Partner Name", "supplier": True,}
)
self.invoice = self.env["account.invoice"].create(
{ {
"partner_id": partner.id, "name": "test spread account_receivable",
"account_id": self.invoice_account.id, "code": "766spread",
"type": "in_invoice", "user_type_id": self.env.ref(
} "account.data_account_type_other_income"
) ).id,
self.invoice_line = self.env["account.invoice.line"].create( "reconcile": True,
{
"quantity": 1.0,
"price_unit": 1000.0,
"invoice_id": self.invoice.id,
"name": "product that cost 1000",
"account_id": self.invoice_account.id,
} }
) )
# Invoices
self.vendor_bill = self.create_account_invoice("in_invoice")
self.sale_invoice = self.create_account_invoice("out_invoice")
self.vendor_bill_line = self.vendor_bill.invoice_line_ids[0]
self.invoice_line = self.sale_invoice.invoice_line_ids[0]
# Set accounts to reconcilable
self.vendor_bill_line.account_id.reconcile = True
self.invoice_line.account_id.reconcile = True
analytic_tags = [(6, 0, self.env.ref("analytic.tag_contract").ids)] analytic_tags = [(6, 0, self.env.ref("analytic.tag_contract").ids)]
self.analytic_account = self.env["account.analytic.account"].create( self.analytic_account = self.env["account.analytic.account"].create(
{"name": "test account",} {"name": "test account"}
) )
self.spread = ( self.spread = (
self.env["account.spread"] self.env["account.spread"]
@ -99,13 +108,13 @@ class TestAccountInvoiceSpread(common.TransactionCase):
[ [
{ {
"name": "test", "name": "test",
"debit_account_id": self.spread_account.id, "debit_account_id": spread_account_payable.id,
"credit_account_id": self.invoice_line_account.id, "credit_account_id": self.account_payable.id,
"period_number": 12, "period_number": 12,
"period_type": "month", "period_type": "month",
"spread_date": datetime.date(2017, 2, 1), "spread_date": datetime.date(2017, 2, 1),
"estimated_amount": 1000.0, "estimated_amount": 1000.0,
"journal_id": self.invoice.journal_id.id, "journal_id": self.vendor_bill.journal_id.id,
"invoice_type": "in_invoice", "invoice_type": "in_invoice",
"account_analytic_id": self.analytic_account.id, "account_analytic_id": self.analytic_account.id,
"analytic_tag_ids": analytic_tags, "analytic_tag_ids": analytic_tags,
@ -114,149 +123,124 @@ class TestAccountInvoiceSpread(common.TransactionCase):
) )
) )
self.invoice_2 = self.env["account.invoice"].create(
{
"partner_id": partner.id,
"account_id": self.invoice_account.id,
"type": "out_invoice",
}
)
self.invoice_line_2 = self.env["account.invoice.line"].create(
{
"quantity": 1.0,
"price_unit": 1000.0,
"invoice_id": self.invoice_2.id,
"name": "product that cost 1000",
"account_id": self.invoice_line_account.id,
}
)
self.spread2 = self.env["account.spread"].create( self.spread2 = self.env["account.spread"].create(
[ [
{ {
"name": "test2", "name": "test2",
"debit_account_id": self.spread_account.id, "debit_account_id": spread_account_receivable.id,
"credit_account_id": self.invoice_line_account.id, "credit_account_id": self.account_receivable.id,
"period_number": 12, "period_number": 12,
"period_type": "month", "period_type": "month",
"spread_date": datetime.date(2017, 2, 1), "spread_date": datetime.date(2017, 2, 1),
"estimated_amount": 1000.0, "estimated_amount": 1000.0,
"journal_id": self.invoice_2.journal_id.id, "journal_id": self.sale_invoice.journal_id.id,
"invoice_type": "out_invoice", "invoice_type": "out_invoice",
} }
] ]
) )
def test_01_wizard_defaults(self): def test_01_wizard_defaults(self):
my_company = self.env.user.company_id
Wizard = self.env["account.spread.invoice.line.link.wizard"] Wizard = self.env["account.spread.invoice.line.link.wizard"]
wizard1 = Wizard.with_context( wizard1 = Wizard.with_context(
default_invoice_line_id=self.invoice_line.id, default_invoice_line_id=self.vendor_bill_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
allow_spread_planning=True, allow_spread_planning=True,
).create({}) ).create({})
self.assertEqual(wizard1.invoice_line_id, self.invoice_line) self.assertEqual(wizard1.invoice_line_id, self.vendor_bill_line)
self.assertEqual(wizard1.invoice_line_id.invoice_id, self.invoice) self.assertEqual(wizard1.invoice_line_id.move_id, self.vendor_bill)
self.assertEqual(wizard1.invoice_type, "in_invoice") self.assertEqual(wizard1.invoice_type, "in_invoice")
self.assertFalse(wizard1.spread_id) self.assertFalse(wizard1.spread_id)
self.assertEqual(wizard1.company_id, my_company) self.assertEqual(wizard1.company_id, self.env.company)
self.assertEqual(wizard1.spread_action_type, "link") self.assertEqual(wizard1.spread_action_type, "link")
self.assertFalse(wizard1.spread_account_id) self.assertFalse(wizard1.spread_account_id)
self.assertFalse(wizard1.spread_journal_id) self.assertFalse(wizard1.spread_journal_id)
wizard2 = Wizard.with_context( wizard2 = Wizard.with_context(
default_invoice_line_id=self.invoice_line_2.id, default_invoice_line_id=self.invoice_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
).create({}) ).create({})
self.assertEqual(wizard2.invoice_line_id, self.invoice_line_2) self.assertEqual(wizard2.invoice_line_id, self.invoice_line)
self.assertEqual(wizard2.invoice_line_id.invoice_id, self.invoice_2) self.assertEqual(wizard2.invoice_line_id.move_id, self.sale_invoice)
self.assertEqual(wizard2.invoice_type, "out_invoice") self.assertEqual(wizard2.invoice_type, "out_invoice")
self.assertFalse(wizard2.spread_id) self.assertFalse(wizard2.spread_id)
self.assertEqual(wizard2.company_id, my_company) self.assertEqual(wizard2.company_id, self.env.company)
self.assertEqual(wizard2.spread_action_type, "template") self.assertEqual(wizard2.spread_action_type, "template")
self.assertFalse(wizard2.spread_account_id) self.assertFalse(wizard2.spread_account_id)
self.assertFalse(wizard2.spread_journal_id) self.assertFalse(wizard2.spread_journal_id)
def test_02_wizard_defaults(self): def test_02_wizard_defaults(self):
my_company = self.env.user.company_id
Wizard = self.env["account.spread.invoice.line.link.wizard"] Wizard = self.env["account.spread.invoice.line.link.wizard"]
account_revenue = self.account_revenue
account_payable = self.account_payable
exp_journal = self.ref("account_spread_cost_revenue.expenses_journal") exp_journal = self.ref("account_spread_cost_revenue.expenses_journal")
sales_journal = self.ref("account_spread_cost_revenue.sales_journal") sales_journal = self.ref("account_spread_cost_revenue.sales_journal")
my_company.default_spread_revenue_account_id = account_revenue self.env.company.default_spread_revenue_account_id = self.account_receivable
my_company.default_spread_expense_account_id = account_payable self.env.company.default_spread_expense_account_id = self.account_payable
my_company.default_spread_revenue_journal_id = sales_journal self.env.company.default_spread_revenue_journal_id = sales_journal
my_company.default_spread_expense_journal_id = exp_journal self.env.company.default_spread_expense_journal_id = exp_journal
self.assertTrue(my_company.default_spread_revenue_account_id) self.assertTrue(self.env.company.default_spread_revenue_account_id)
self.assertTrue(my_company.default_spread_expense_account_id) self.assertTrue(self.env.company.default_spread_expense_account_id)
self.assertTrue(my_company.default_spread_revenue_journal_id) self.assertTrue(self.env.company.default_spread_revenue_journal_id)
self.assertTrue(my_company.default_spread_expense_journal_id) self.assertTrue(self.env.company.default_spread_expense_journal_id)
wizard1 = Wizard.with_context( wizard1 = Wizard.with_context(
default_invoice_line_id=self.invoice_line.id, default_invoice_line_id=self.vendor_bill_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
allow_spread_planning=True, allow_spread_planning=True,
).create({}) ).create({})
self.assertEqual(wizard1.invoice_line_id, self.invoice_line) self.assertEqual(wizard1.invoice_line_id, self.vendor_bill_line)
self.assertEqual(wizard1.invoice_line_id.invoice_id, self.invoice) self.assertEqual(wizard1.invoice_line_id.move_id, self.vendor_bill)
self.assertEqual(wizard1.invoice_type, "in_invoice") self.assertEqual(wizard1.invoice_type, "in_invoice")
self.assertFalse(wizard1.spread_id) self.assertFalse(wizard1.spread_id)
self.assertEqual(wizard1.company_id, my_company) self.assertEqual(wizard1.company_id, self.env.company)
self.assertEqual(wizard1.spread_action_type, "link") self.assertEqual(wizard1.spread_action_type, "link")
self.assertFalse(wizard1.spread_account_id)
self.assertFalse(wizard1.spread_journal_id)
res_onchange = wizard1.onchange_invoice_type()
self.assertTrue(res_onchange)
self.assertTrue(res_onchange.get("domain"))
wizard1._onchange_spread_journal_account()
self.assertTrue(wizard1.spread_account_id) self.assertTrue(wizard1.spread_account_id)
self.assertTrue(wizard1.spread_journal_id) self.assertTrue(wizard1.spread_journal_id)
self.assertEqual(wizard1.spread_account_id, account_payable) self.assertEqual(wizard1.spread_account_id, self.account_payable)
self.assertEqual(wizard1.spread_journal_id.id, exp_journal) self.assertEqual(wizard1.spread_journal_id.id, exp_journal)
self.assertTrue(wizard1.spread_invoice_type_domain_ids)
wizard2 = Wizard.with_context( wizard2 = Wizard.with_context(
default_invoice_line_id=self.invoice_line_2.id, default_invoice_line_id=self.invoice_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
).create({}) ).create({})
self.assertEqual(wizard2.invoice_line_id, self.invoice_line_2) self.assertEqual(wizard2.invoice_line_id, self.invoice_line)
self.assertEqual(wizard2.invoice_line_id.invoice_id, self.invoice_2) self.assertEqual(wizard2.invoice_line_id.move_id, self.sale_invoice)
self.assertEqual(wizard2.invoice_type, "out_invoice") self.assertEqual(wizard2.invoice_type, "out_invoice")
self.assertFalse(wizard2.spread_id) self.assertFalse(wizard2.spread_id)
self.assertEqual(wizard2.company_id, my_company) self.assertEqual(wizard2.company_id, self.env.company)
self.assertEqual(wizard2.spread_action_type, "template") self.assertEqual(wizard2.spread_action_type, "template")
self.assertFalse(wizard2.spread_account_id)
self.assertFalse(wizard2.spread_journal_id)
res_onchange = wizard2.onchange_invoice_type()
self.assertTrue(res_onchange)
self.assertTrue(res_onchange.get("domain"))
wizard2._onchange_spread_journal_account()
self.assertTrue(wizard2.spread_account_id) self.assertTrue(wizard2.spread_account_id)
self.assertTrue(wizard2.spread_journal_id) self.assertTrue(wizard2.spread_journal_id)
self.assertEqual(wizard2.spread_account_id, account_revenue) self.assertEqual(wizard2.spread_account_id, self.account_receivable)
self.assertEqual(wizard2.spread_journal_id.id, sales_journal) self.assertEqual(wizard2.spread_journal_id.id, sales_journal)
self.assertTrue(wizard2.spread_invoice_type_domain_ids)
def test_03_link_invoice_line_with_spread_sheet(self): def test_03_link_invoice_line_with_spread_sheet(self):
my_company = self.env.user.company_id self.env.user.write(
{
"groups_id": [
(4, self.env.ref("analytic.group_analytic_accounting").id),
(4, self.env.ref("analytic.group_analytic_tags").id),
],
}
)
Wizard = self.env["account.spread.invoice.line.link.wizard"] Wizard = self.env["account.spread.invoice.line.link.wizard"]
wizard1 = Wizard.with_context( wizard1 = Wizard.with_context(
default_invoice_line_id=self.invoice_line.id, default_invoice_line_id=self.vendor_bill_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
allow_spread_planning=True, allow_spread_planning=True,
).create({}) ).create({})
self.assertEqual(wizard1.spread_action_type, "link") self.assertEqual(wizard1.spread_action_type, "link")
wizard1.spread_account_id = self.account_revenue wizard1.spread_account_id = self.account_receivable
wizard1.spread_journal_id = self.ref( wizard1.spread_journal_id = self.ref(
"account_spread_cost_revenue.expenses_journal" "account_spread_cost_revenue.expenses_journal"
) )
@ -266,27 +250,21 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(res_action.get("res_id")) self.assertTrue(res_action.get("res_id"))
self.assertEqual(res_action.get("res_id"), self.spread.id) self.assertEqual(res_action.get("res_id"), self.spread.id)
self.assertTrue(self.spread.invoice_line_id) self.assertTrue(self.spread.invoice_line_id)
self.assertEqual(self.spread.invoice_line_id, self.invoice_line) self.assertEqual(self.spread.invoice_line_id, self.vendor_bill_line)
spread_lines = self.spread.line_ids self.assertFalse(any(line.move_id for line in self.spread.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.invoice.journal_id.update_posted = True
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
for line in spread_lines: for line in spread_lines:
line.create_move() line.create_move()
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
self.assertTrue(line.move_id.journal_id.update_posted)
for ml in line.move_id.line_ids: for ml in line.move_id.line_ids:
ml_analytic_account = ml.analytic_account_id
analytic_tag = self.env.ref("analytic.tag_contract") analytic_tag = self.env.ref("analytic.tag_contract")
self.assertEqual(ml_analytic_account, self.analytic_account) self.assertEqual(ml.analytic_account_id, self.analytic_account)
self.assertEqual(ml.analytic_tag_ids, analytic_tag) self.assertEqual(ml.analytic_tag_ids, analytic_tag)
self.spread.invoice_id.action_cancel() self.spread.invoice_id.button_cancel()
self.assertTrue(self.spread.invoice_line_id) self.assertTrue(self.spread.invoice_line_id)
with self.assertRaises(UserError): with self.assertRaises(UserError):
@ -297,21 +275,19 @@ class TestAccountInvoiceSpread(common.TransactionCase):
def test_04_new_spread_sheet(self): def test_04_new_spread_sheet(self):
my_company = self.env.user.company_id
Wizard = self.env["account.spread.invoice.line.link.wizard"] Wizard = self.env["account.spread.invoice.line.link.wizard"]
spread_account = self.account_revenue
spread_journal_id = self.ref("account_spread_cost_revenue.expenses_journal") spread_journal_id = self.ref("account_spread_cost_revenue.expenses_journal")
wizard1 = Wizard.with_context( wizard1 = Wizard.with_context(
default_invoice_line_id=self.invoice_line.id, default_invoice_line_id=self.vendor_bill_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
).create({"spread_action_type": "new",}) ).create({"spread_action_type": "new"})
self.assertEqual(wizard1.spread_action_type, "new") self.assertEqual(wizard1.spread_action_type, "new")
wizard1.write( wizard1.write(
{ {
"spread_account_id": spread_account.id, "spread_account_id": self.account_receivable.id,
"spread_journal_id": spread_journal_id, "spread_journal_id": spread_journal_id,
} }
) )
@ -327,9 +303,7 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(res_context.get("default_debit_account_id")) self.assertTrue(res_context.get("default_debit_account_id"))
self.assertTrue(res_context.get("default_credit_account_id")) self.assertTrue(res_context.get("default_credit_account_id"))
spread_lines = self.spread.line_ids self.assertFalse(any(line.move_id for line in self.spread.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
@ -338,14 +312,14 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
wizard2 = Wizard.with_context( wizard2 = Wizard.with_context(
default_invoice_line_id=self.invoice_line_2.id, default_invoice_line_id=self.invoice_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
).create({"spread_action_type": "new",}) ).create({"spread_action_type": "new"})
self.assertEqual(wizard2.spread_action_type, "new") self.assertEqual(wizard2.spread_action_type, "new")
wizard2.write( wizard2.write(
{ {
"spread_account_id": spread_account.id, "spread_account_id": self.account_receivable.id,
"spread_journal_id": spread_journal_id, "spread_journal_id": spread_journal_id,
} }
) )
@ -361,18 +335,15 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(res_context.get("default_debit_account_id")) self.assertTrue(res_context.get("default_debit_account_id"))
self.assertTrue(res_context.get("default_credit_account_id")) self.assertTrue(res_context.get("default_credit_account_id"))
spread_lines = self.spread2.line_ids self.assertFalse(any(line.move_id for line in self.spread2.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread2.compute_spread_board() self.spread2.compute_spread_board()
for line in spread_lines: for line in self.spread2.line_ids:
line.create_move() line.create_move()
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
def test_05_new_spread_sheet_from_template(self): def test_05_new_spread_sheet_from_template(self):
my_company = self.env.user.company_id
Wizard = self.env["account.spread.invoice.line.link.wizard"] Wizard = self.env["account.spread.invoice.line.link.wizard"]
spread_account = self.account_payable spread_account = self.account_payable
@ -391,29 +362,28 @@ class TestAccountInvoiceSpread(common.TransactionCase):
) )
wizard1 = Wizard.with_context( wizard1 = Wizard.with_context(
default_invoice_line_id=self.invoice_line.id, default_invoice_line_id=self.vendor_bill_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
).create({"spread_action_type": "template", "template_id": template.id,}) ).create({"spread_action_type": "template", "template_id": template.id})
self.assertEqual(wizard1.spread_action_type, "template") self.assertEqual(wizard1.spread_action_type, "template")
res_action = wizard1.confirm() res_action = wizard1.confirm()
self.assertTrue(isinstance(res_action, dict)) self.assertTrue(isinstance(res_action, dict))
self.assertTrue(res_action.get("res_id")) self.assertTrue(res_action.get("res_id"))
spread_lines = self.spread.line_ids new_spread = self.env["account.spread"].browse(res_action["res_id"])
for line in spread_lines: new_spread.compute_spread_board()
self.assertFalse(line.move_id)
self.spread.compute_spread_board() self.assertFalse(any(line.move_id for line in new_spread.line_ids))
spread_lines = self.spread.line_ids
for line in spread_lines: for line in new_spread.line_ids:
line.create_move() line.create_move()
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
wizard2 = Wizard.with_context( wizard2 = Wizard.with_context(
default_invoice_line_id=self.invoice_line_2.id, default_invoice_line_id=self.invoice_line.id,
default_company_id=my_company.id, default_company_id=self.env.company.id,
).create({"spread_action_type": "new",}) ).create({"spread_action_type": "new"})
self.assertEqual(wizard2.spread_action_type, "new") self.assertEqual(wizard2.spread_action_type, "new")
wizard2.write( wizard2.write(
@ -434,18 +404,16 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(res_context.get("default_debit_account_id")) self.assertTrue(res_context.get("default_debit_account_id"))
self.assertTrue(res_context.get("default_credit_account_id")) self.assertTrue(res_context.get("default_credit_account_id"))
spread_lines = self.spread2.line_ids self.assertFalse(any(line.move_id for line in self.spread2.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread2.compute_spread_board() self.spread2.compute_spread_board()
for line in spread_lines: for line in self.spread2.line_ids:
line.create_move() line.create_move()
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
def test_06_open_wizard(self): def test_06_open_wizard(self):
res_action = self.invoice_line.spread_details() res_action = self.vendor_bill_line.spread_details()
self.assertTrue(isinstance(res_action, dict)) self.assertTrue(isinstance(res_action, dict))
self.assertFalse(res_action.get("res_id")) self.assertFalse(res_action.get("res_id"))
self.assertTrue(res_action.get("context")) self.assertTrue(res_action.get("context"))
@ -453,28 +421,38 @@ class TestAccountInvoiceSpread(common.TransactionCase):
def test_07_unlink_invoice_line_and_spread_sheet(self): def test_07_unlink_invoice_line_and_spread_sheet(self):
self.assertFalse(self.spread.invoice_line_id) self.assertFalse(self.spread.invoice_line_id)
self.invoice_line.spread_id = self.spread
self.vendor_bill_line.spread_id = self.spread
self.assertTrue(self.spread.invoice_line_id) self.assertTrue(self.spread.invoice_line_id)
self.spread.action_unlink_invoice_line() self.spread.action_unlink_invoice_line()
self.assertFalse(self.spread.invoice_line_id) self.assertFalse(self.spread.invoice_line_id)
self.assertFalse(self.spread2.invoice_line_id) self.assertFalse(self.spread2.invoice_line_id)
self.invoice_line_2.spread_id = self.spread2 self.invoice_line.spread_id = self.spread2
self.assertTrue(self.spread2.invoice_line_id) self.assertTrue(self.spread2.invoice_line_id)
self.spread2.action_unlink_invoice_line() self.spread2.action_unlink_invoice_line()
self.assertFalse(self.spread2.invoice_line_id) self.assertFalse(self.spread2.invoice_line_id)
def test_08_invoice_multi_line(self): def test_08_invoice_multi_line(self):
self.invoice_line.copy() invoice_form = Form(self.vendor_bill)
self.assertEqual(len(self.invoice.invoice_line_ids), 2) with invoice_form.invoice_line_ids.new() as line:
line.name = "new test line"
line.quantity = 1.0
line.price_unit = 1000.0
self.invoice = invoice_form.save()
self.assertEqual(len(self.vendor_bill.invoice_line_ids), 2)
self.invoice.invoice_line_ids[0].spread_id = self.spread self.vendor_bill_line.spread_id = self.spread
self.assertTrue(self.spread.invoice_line_id) self.assertTrue(self.spread.invoice_id.invoice_line_ids[0])
self.assertEqual(self.spread.invoice_line_id, self.invoice_line) self.assertEqual(
self.spread.invoice_id.invoice_line_ids[0], self.vendor_bill_line
)
self.assertTrue(self.vendor_bill_line.spread_id)
self.assertEqual(self.vendor_bill_line.spread_check, "linked")
self.assertFalse(self.vendor_bill.invoice_line_ids[1].spread_id)
self.assertEqual(self.vendor_bill.invoice_line_ids[1].spread_check, "unlinked")
spread_lines = self.spread.line_ids self.assertFalse(any(line.move_id for line in self.spread.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
@ -483,39 +461,47 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
# Validate invoice # Validate invoice
self.invoice.action_invoice_open() self.vendor_bill.action_post()
self.assertTrue(self.vendor_bill_line.spread_id)
self.assertEqual(self.vendor_bill_line.spread_check, "linked")
self.assertFalse(self.vendor_bill.invoice_line_ids[1].spread_id)
self.assertEqual(
self.vendor_bill.invoice_line_ids[1].spread_check, "unavailable"
)
def test_09_no_link_invoice(self): def test_09_no_link_invoice(self):
balance_sheet = self.spread.credit_account_id balance_sheet = self.spread.credit_account_id
# Validate invoice # Validate invoice
self.invoice.action_invoice_open() self.vendor_bill.action_post()
invoice_mls = self.invoice.move_id.mapped("line_ids") invoice_mls = self.vendor_bill.invoice_line_ids
self.assertTrue(invoice_mls) self.assertTrue(invoice_mls)
for invoice_ml in invoice_mls: for invoice_ml in invoice_mls:
if invoice_ml.debit: self.assertNotEqual(invoice_ml.account_id, balance_sheet)
self.assertNotEqual(invoice_ml.account_id, balance_sheet)
def test_10_link_vendor_bill_line_with_spread_sheet(self): def test_10_link_vendor_bill_line_with_spread_sheet(self):
invoice_form = Form(self.vendor_bill)
with invoice_form.invoice_line_ids.new() as line:
line.name = "new test line"
line.quantity = 1.0
line.price_unit = 1000.0
self.invoice = invoice_form.save()
copied_line = self.invoice_line.copy()
copied_line.name = "new test line"
self.spread.write( self.spread.write(
{ {
"estimated_amount": 1000.0, "estimated_amount": 1000.0,
"period_number": 12, "period_number": 12,
"period_type": "month", "period_type": "month",
"spread_date": datetime.date(2017, 1, 7), "spread_date": datetime.date(2017, 1, 7),
"invoice_line_id": self.invoice_line.id, "invoice_line_id": self.vendor_bill_line.id,
"move_line_auto_post": False, "move_line_auto_post": False,
} }
) )
spread_lines = self.spread.line_ids self.assertFalse(any(line.move_id for line in self.spread.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
@ -536,13 +522,10 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertEqual(spread_ml.account_id, balance_sheet) self.assertEqual(spread_ml.account_id, balance_sheet)
# Validate invoice # Validate invoice
self.invoice.action_invoice_open() self.vendor_bill.action_post()
invoice_mls = self.invoice.move_id.mapped("line_ids")
self.assertTrue(invoice_mls)
count_balance_sheet = len( count_balance_sheet = len(
invoice_mls.filtered(lambda x: x.account_id == balance_sheet) self.vendor_bill.line_ids.filtered(lambda x: x.account_id == balance_sheet)
) )
self.assertEqual(count_balance_sheet, 1) self.assertEqual(count_balance_sheet, 1)
@ -562,21 +545,24 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertTrue(action_reconcile_view.get("context")) self.assertTrue(action_reconcile_view.get("context"))
def test_11_link_vendor_bill_line_with_spread_sheet(self): def test_11_link_vendor_bill_line_with_spread_sheet(self):
self.invoice_line.copy() invoice_form = Form(self.vendor_bill)
with invoice_form.invoice_line_ids.new() as line:
line.name = "new test line"
line.quantity = 1.0
line.price_unit = 1000.0
self.invoice = invoice_form.save()
self.spread.write( self.spread.write(
{ {
"estimated_amount": 1000.0, "estimated_amount": 1000.0,
"period_number": 12, "period_number": 12,
"period_type": "month", "period_type": "month",
"spread_date": datetime.date(2017, 1, 7), "spread_date": datetime.date(2017, 1, 7),
"invoice_line_id": self.invoice_line.id, "invoice_line_id": self.vendor_bill_line.id,
"move_line_auto_post": False, "move_line_auto_post": False,
} }
) )
spread_lines = self.spread.line_ids self.assertFalse(any(line.move_id for line in self.spread.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
@ -597,9 +583,9 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertEqual(spread_ml.account_id, balance_sheet) self.assertEqual(spread_ml.account_id, balance_sheet)
# Validate invoice # Validate invoice
self.invoice.action_invoice_open() self.vendor_bill.action_post()
invoice_mls = self.invoice.move_id.mapped("line_ids") invoice_mls = self.vendor_bill.line_ids
self.assertTrue(invoice_mls) self.assertTrue(invoice_mls)
count_balance_sheet = len( count_balance_sheet = len(
@ -613,33 +599,39 @@ class TestAccountInvoiceSpread(common.TransactionCase):
spread_mls = self.spread.line_ids.mapped("move_id.line_ids") spread_mls = self.spread.line_ids.mapped("move_id.line_ids")
self.assertTrue(spread_mls) self.assertTrue(spread_mls)
for spread_ml in spread_mls: for spread_ml in spread_mls:
self.assertFalse(spread_ml.full_reconcile_id) if spread_ml.credit:
self.assertEqual(spread_ml.account_id, balance_sheet)
self.assertTrue(spread_ml.full_reconcile_id)
if spread_ml.debit:
self.assertEqual(spread_ml.account_id, expense_account)
self.assertFalse(spread_ml.full_reconcile_id)
action_reconcile_view = self.spread.open_reconcile_view() action_reconcile_view = self.spread.open_reconcile_view()
self.assertTrue(isinstance(action_reconcile_view, dict)) self.assertTrue(isinstance(action_reconcile_view, dict))
self.assertTrue(action_reconcile_view.get("domain")[0][2]) self.assertTrue(action_reconcile_view.get("domain")[0][2])
self.assertTrue(action_reconcile_view.get("context")) self.assertTrue(action_reconcile_view.get("context"))
action_spread_details = self.invoice_line.spread_details() action_spread_details = self.vendor_bill_line.spread_details()
self.assertTrue(isinstance(action_spread_details, dict)) self.assertTrue(isinstance(action_spread_details, dict))
self.assertTrue(action_spread_details.get("res_id")) self.assertTrue(action_spread_details.get("res_id"))
def test_12_link_invoice_line_with_spread_sheet_full_reconcile(self): def test_12_link_invoice_line_with_spread_sheet_full_reconcile(self):
# Validate invoice
self.sale_invoice.action_post()
self.spread2.write( self.spread2.write(
{ {
"estimated_amount": 1000.0, "estimated_amount": 1000.0,
"period_number": 12, "period_number": 12,
"period_type": "month", "period_type": "month",
"spread_date": datetime.date(2017, 1, 7), "spread_date": datetime.date(2017, 1, 7),
"invoice_line_id": self.invoice_line_2.id, "invoice_line_id": self.invoice_line.id,
"move_line_auto_post": False, "move_line_auto_post": False,
} }
) )
spread_lines = self.spread2.line_ids self.assertFalse(any(line.move_id for line in self.spread2.line_ids))
for line in spread_lines:
self.assertFalse(line.move_id)
self.spread2.compute_spread_board() self.spread2.compute_spread_board()
spread_lines = self.spread2.line_ids spread_lines = self.spread2.line_ids
@ -647,8 +639,8 @@ class TestAccountInvoiceSpread(common.TransactionCase):
line.create_move() line.create_move()
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
payable_account = self.spread.credit_account_id payable_account = self.spread2.credit_account_id
balance_sheet = self.spread.debit_account_id balance_sheet = self.spread2.debit_account_id
self.assertTrue(balance_sheet.reconcile) self.assertTrue(balance_sheet.reconcile)
spread_mls = self.spread2.line_ids.mapped("move_id.line_ids") spread_mls = self.spread2.line_ids.mapped("move_id.line_ids")
@ -656,35 +648,25 @@ class TestAccountInvoiceSpread(common.TransactionCase):
for spread_ml in spread_mls: for spread_ml in spread_mls:
if spread_ml.debit: if spread_ml.debit:
self.assertEqual(spread_ml.account_id, balance_sheet) self.assertEqual(spread_ml.account_id, balance_sheet)
if spread_ml.credit: self.assertTrue(spread_ml.reconciled)
self.assertEqual(spread_ml.account_id, payable_account)
# Validate invoice
self.invoice_2.action_invoice_open()
invoice_mls = self.invoice_2.move_id.mapped("line_ids")
self.assertTrue(invoice_mls)
for invoice_ml in invoice_mls:
if invoice_ml.credit:
self.assertEqual(invoice_ml.account_id, balance_sheet)
self.spread2.line_ids.create_and_reconcile_moves()
spread_mls = self.spread2.line_ids.mapped("move_id.line_ids")
self.assertTrue(spread_mls)
for spread_ml in spread_mls:
if spread_ml.debit:
self.assertTrue(spread_ml.full_reconcile_id) self.assertTrue(spread_ml.full_reconcile_id)
if spread_ml.credit: if spread_ml.credit:
self.assertEqual(spread_ml.account_id, payable_account)
self.assertFalse(spread_ml.reconciled)
self.assertFalse(spread_ml.full_reconcile_id) self.assertFalse(spread_ml.full_reconcile_id)
invoice_mls = self.sale_invoice.invoice_line_ids
self.assertTrue(invoice_mls)
for invoice_ml in invoice_mls:
self.assertEqual(invoice_ml.account_id, balance_sheet)
action_reconcile_view = self.spread2.open_reconcile_view() action_reconcile_view = self.spread2.open_reconcile_view()
self.assertTrue(isinstance(action_reconcile_view, dict)) self.assertTrue(isinstance(action_reconcile_view, dict))
self.assertTrue(action_reconcile_view.get("domain")[0][2]) self.assertTrue(action_reconcile_view.get("domain")[0][2])
self.assertFalse(action_reconcile_view.get("res_id")) self.assertFalse(action_reconcile_view.get("res_id"))
self.assertTrue(action_reconcile_view.get("context")) self.assertTrue(action_reconcile_view.get("context"))
action_spread_details = self.invoice_line_2.spread_details() action_spread_details = self.invoice_line.spread_details()
self.assertTrue(isinstance(action_spread_details, dict)) self.assertTrue(isinstance(action_spread_details, dict))
self.assertTrue(action_spread_details.get("res_id")) self.assertTrue(action_spread_details.get("res_id"))
@ -702,19 +684,18 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.spread2.compute_spread_board() self.spread2.compute_spread_board()
spread_lines = self.spread2.line_ids spread_lines = self.spread2.line_ids
self.assertEqual(len(spread_lines), 13) self.assertEqual(len(spread_lines), 13)
self.assertFalse(any(line.move_id for line in spread_lines))
for line in spread_lines: spread_lines[0].create_and_reconcile_moves()
self.assertFalse(line.move_id) spread_lines[1].create_and_reconcile_moves()
spread_lines[2].create_and_reconcile_moves()
spread_lines[0]._create_moves().post() spread_lines[3].create_and_reconcile_moves()
spread_lines[1]._create_moves().post()
spread_lines[2]._create_moves().post()
spread_lines[3]._create_moves().post()
self.assertEqual(spread_lines[0].move_id.state, "posted") self.assertEqual(spread_lines[0].move_id.state, "posted")
self.assertEqual(spread_lines[1].move_id.state, "posted") self.assertEqual(spread_lines[1].move_id.state, "posted")
self.assertEqual(spread_lines[2].move_id.state, "posted") self.assertEqual(spread_lines[2].move_id.state, "posted")
self.assertEqual(spread_lines[3].move_id.state, "posted") self.assertEqual(spread_lines[3].move_id.state, "posted")
self.assertNotEqual(spread_lines[4].move_id.state, "posted")
spread_mls = spread_lines[0].move_id.line_ids spread_mls = spread_lines[0].move_id.line_ids
self.assertTrue(spread_mls) self.assertTrue(spread_mls)
@ -728,23 +709,26 @@ class TestAccountInvoiceSpread(common.TransactionCase):
self.assertFalse(spread_ml.matched_credit_ids) self.assertFalse(spread_ml.matched_credit_ids)
self.assertFalse(spread_ml.full_reconcile_id) self.assertFalse(spread_ml.full_reconcile_id)
balance_sheet = self.spread.debit_account_id balance_sheet = self.spread2.debit_account_id
self.assertTrue(balance_sheet.reconcile) self.assertTrue(balance_sheet.reconcile)
self.spread2.write( # Assing invoice line to spread
{"invoice_line_id": self.invoice_line_2.id,} self.spread2.invoice_line_id = self.invoice_line
) self.assertEqual(self.invoice_line.spread_id, self.spread2)
# Validate invoice # Validate invoice
self.invoice_2.action_invoice_open() self.sale_invoice.action_post()
invoice_mls = self.sale_invoice.invoice_line_ids
invoice_mls = self.invoice_2.move_id.mapped("line_ids")
self.assertTrue(invoice_mls) self.assertTrue(invoice_mls)
for invoice_ml in invoice_mls: for invoice_ml in invoice_mls:
if invoice_ml.credit: self.assertEqual(invoice_ml.account_id, balance_sheet)
self.assertEqual(invoice_ml.account_id, balance_sheet)
spread_mls = spread_lines[0].move_id.line_ids spread_lines = self.spread2.line_ids
spread_lines[4].create_and_reconcile_moves()
self.assertEqual(spread_lines[4].move_id.state, "posted")
spread_mls = spread_lines[4].move_id.line_ids
self.assertTrue(spread_mls) self.assertTrue(spread_mls)
for spread_ml in spread_mls: for spread_ml in spread_mls:
if spread_ml.debit: if spread_ml.debit:
@ -767,39 +751,37 @@ class TestAccountInvoiceSpread(common.TransactionCase):
def test_14_create_all_moves(self): def test_14_create_all_moves(self):
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids
self.assertEqual(len(spread_lines), 12) self.assertEqual(len(self.spread.line_ids), 12)
for line in spread_lines: self.assertFalse(any(line.move_id for line in self.spread.line_ids))
self.assertFalse(line.move_id)
# create moves for all the spread lines # create moves for all the spread lines
self.spread.create_all_moves() self.spread.create_all_moves()
spread_lines = self.spread.line_ids self.assertTrue(all(line.move_id for line in self.spread.line_ids))
for line in spread_lines:
self.assertTrue(line.move_id)
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
self.spread.unlink() self.spread.unlink()
def test_15_invoice_refund(self): def test_15_invoice_refund(self):
self.invoice_line.spread_id = self.spread self.vendor_bill_line.spread_id = self.spread
# Validate invoice # Validate invoice
self.invoice.action_invoice_open() self.vendor_bill.action_post()
self.assertTrue(self.invoice.invoice_line_ids.mapped("spread_id")) self.assertTrue(self.vendor_bill.invoice_line_ids.mapped("spread_id"))
# Create a refund for invoice. # Create a refund for invoice.
self.env["account.invoice.refund"].with_context( move_reversal = (
{ self.env["account.move.reversal"]
"active_model": "account.invoice", .with_context(active_model="account.move", active_ids=self.vendor_bill.ids)
"active_ids": [self.invoice.id], .create(
"active_id": self.invoice.id, {
} "date": fields.Date.today(),
).create( "reason": "no reason",
dict(description="Invoice Refund", filter_refund="refund",) "refund_method": "refund",
).invoice_refund() }
)
# Invoice lines do not contain the lint to the spread. )
refund = self.invoice.refund_invoice_ids[0] reversal = move_reversal.reverse_moves()
refund = self.env["account.move"].browse(reversal["res_id"])
self.assertFalse(refund.invoice_line_ids.mapped("spread_id")) self.assertFalse(refund.invoice_line_ids.mapped("spread_id"))

View File

@ -1,4 +1,4 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>) # Copyright 2018-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import datetime import datetime
@ -6,34 +6,28 @@ import datetime
from psycopg2 import IntegrityError from psycopg2 import IntegrityError
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from odoo.modules.module import get_resource_path from odoo.tests import Form, common
from odoo.tests import common from odoo.tools import mute_logger
from odoo.tools import convert_file, mute_logger
class TestAccountSpreadCostRevenue(common.TransactionCase): class TestAccountSpreadCostRevenue(common.TransactionCase):
def _load(self, module, *args):
convert_file(
self.cr,
"account_spread_cost_revenue",
get_resource_path(module, *args),
{},
"init",
False,
"test",
self.registry._assertion_report,
)
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self._load("account", "test", "account_minimal_test.xml")
self.sales_journal = self.env["account.journal"].create(
{"name": "Customer Invoices - Test", "code": "TEST1", "type": "sale"}
)
self.expenses_journal = self.env["account.journal"].create(
{"name": "Vendor Bills - Test", "code": "TEST2", "type": "purchase"}
)
type_receivable = self.env.ref("account.data_account_type_receivable") type_receivable = self.env.ref("account.data_account_type_receivable")
type_expenses = self.env.ref("account.data_account_type_expenses") type_expenses = self.env.ref("account.data_account_type_expenses")
type_payable = self.env.ref("account.data_account_type_payable") type_payable = self.env.ref("account.data_account_type_payable")
type_revenue = self.env.ref("account.data_account_type_revenue") type_revenue = self.env.ref("account.data_account_type_revenue")
self.account_receivable = self.env["account.account"].create( self.credit_account = self.env["account.account"].create(
{ {
"name": "test_account_receivable", "name": "test_account_receivable",
"code": "123", "code": "123",
@ -41,9 +35,8 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
"reconcile": True, "reconcile": True,
} }
) )
self.credit_account = self.account_receivable
self.account_expenses = self.env["account.account"].create( self.debit_account = self.env["account.account"].create(
{ {
"name": "test account_expenses", "name": "test account_expenses",
"code": "765", "code": "765",
@ -51,7 +44,6 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
"reconcile": True, "reconcile": True,
} }
) )
self.debit_account = self.account_expenses
self.account_payable = self.env["account.account"].create( self.account_payable = self.env["account.account"].create(
{ {
@ -73,6 +65,12 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
def test_01_account_spread_defaults(self): def test_01_account_spread_defaults(self):
this_year = datetime.date.today().year this_year = datetime.date.today().year
spread_template = self.env["account.spread.template"].create(
{"name": "test", "spread_account_id": self.debit_account.id}
)
self.assertEqual(spread_template.spread_type, "sale")
self.assertTrue(spread_template.spread_journal_id)
spread = self.env["account.spread"].create( spread = self.env["account.spread"].create(
{ {
"name": "test", "name": "test",
@ -81,7 +79,6 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
"credit_account_id": self.credit_account.id, "credit_account_id": self.credit_account.id,
} }
) )
my_company = self.env.user.company_id
self.assertTrue(spread) self.assertTrue(spread)
self.assertFalse(spread.line_ids) self.assertFalse(spread.line_ids)
@ -93,8 +90,8 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
self.assertTrue(spread.move_line_auto_post) self.assertTrue(spread.move_line_auto_post)
self.assertEqual(spread.name, "test") self.assertEqual(spread.name, "test")
self.assertEqual(spread.invoice_type, "out_invoice") self.assertEqual(spread.invoice_type, "out_invoice")
self.assertEqual(spread.company_id, my_company) self.assertEqual(spread.company_id, self.env.company)
self.assertEqual(spread.currency_id, my_company.currency_id) self.assertEqual(spread.currency_id, self.env.company.currency_id)
self.assertEqual(spread.period_number, 12) self.assertEqual(spread.period_number, 12)
self.assertEqual(spread.period_type, "month") self.assertEqual(spread.period_type, "month")
self.assertEqual(spread.debit_account_id, self.debit_account) self.assertEqual(spread.debit_account_id, self.debit_account)
@ -112,43 +109,40 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
self.assertTrue(spread.display_move_line_auto_post) self.assertTrue(spread.display_move_line_auto_post)
def test_02_config_defaults(self): def test_02_config_defaults(self):
my_company = self.env.user.company_id self.assertFalse(self.env.company.default_spread_revenue_account_id)
self.assertFalse(my_company.default_spread_revenue_account_id) self.assertFalse(self.env.company.default_spread_expense_account_id)
self.assertFalse(my_company.default_spread_expense_account_id) self.assertFalse(self.env.company.default_spread_revenue_journal_id)
self.assertFalse(my_company.default_spread_revenue_journal_id) self.assertFalse(self.env.company.default_spread_expense_journal_id)
self.assertFalse(my_company.default_spread_expense_journal_id)
@mute_logger("odoo.sql_db") @mute_logger("odoo.sql_db")
def test_03_no_defaults(self): def test_03_no_defaults(self):
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
self.env["account.spread"].create( self.env["account.spread"].create({"name": "test"})
{"name": "test",}
)
@mute_logger("odoo.sql_db") @mute_logger("odoo.sql_db")
def test_04_no_defaults(self): def test_04_no_defaults(self):
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
self.env["account.spread"].create( self.env["account.spread"].create(
{"name": "test", "invoice_type": "out_invoice",} {"name": "test", "invoice_type": "out_invoice"}
) )
def test_05_config_settings(self): def test_05_config_settings(self):
my_company = self.env.user.company_id self.env.company.default_spread_revenue_account_id = self.account_revenue
self.env.company.default_spread_expense_account_id = self.account_payable
self.env.company.default_spread_revenue_journal_id = self.sales_journal
self.env.company.default_spread_expense_journal_id = self.expenses_journal
account_revenue = self.account_revenue self.assertTrue(self.env.company.default_spread_revenue_account_id)
exp_journal = self.ref("account_spread_cost_revenue.expenses_journal") self.assertTrue(self.env.company.default_spread_expense_account_id)
sales_journal = self.ref("account_spread_cost_revenue.sales_journal") self.assertTrue(self.env.company.default_spread_revenue_journal_id)
my_company.default_spread_revenue_account_id = account_revenue self.assertTrue(self.env.company.default_spread_expense_journal_id)
my_company.default_spread_expense_account_id = self.account_payable
my_company.default_spread_revenue_journal_id = sales_journal
my_company.default_spread_expense_journal_id = exp_journal
self.assertTrue(my_company.default_spread_revenue_account_id) spread_form = Form(self.env["account.spread"])
self.assertTrue(my_company.default_spread_expense_account_id) spread_form.name = "test"
self.assertTrue(my_company.default_spread_revenue_journal_id) spread_form.invoice_type = "in_invoice"
self.assertTrue(my_company.default_spread_expense_journal_id) spread_form.debit_account_id = self.debit_account
spread_form.credit_account_id = self.credit_account
spread = self.env["account.spread"].new({"name": "test",}) spread = spread_form.save()
self.assertTrue(spread) self.assertTrue(spread)
self.assertFalse(spread.line_ids) self.assertFalse(spread.line_ids)
@ -157,106 +151,52 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
self.assertFalse(spread.invoice_id) self.assertFalse(spread.invoice_id)
self.assertFalse(spread.account_analytic_id) self.assertFalse(spread.account_analytic_id)
self.assertFalse(spread.analytic_tag_ids) self.assertFalse(spread.analytic_tag_ids)
self.assertFalse(spread.move_line_auto_post) self.assertTrue(spread.move_line_auto_post)
defaults = self.env["account.spread"].default_get( defaults = self.env["account.spread"].default_get(["company_id", "currency_id"])
["company_id", "currency_id",]
)
self.assertEqual(defaults["company_id"], my_company.id) self.assertEqual(defaults["company_id"], self.env.company.id)
self.assertEqual(defaults["currency_id"], my_company.currency_id.id) self.assertEqual(defaults["currency_id"], self.env.company.currency_id.id)
spread.invoice_type = "out_invoice" spread_form = Form(spread)
spread.company_id = my_company spread_form.invoice_type = "out_invoice"
spread.onchange_invoice_type() spread_form.company_id = self.env.company
self.assertEqual(spread.debit_account_id, account_revenue) spread = spread_form.save()
self.assertFalse(spread.credit_account_id) self.assertEqual(spread.debit_account_id, self.account_revenue)
self.assertEqual(spread.journal_id.id, sales_journal) self.assertFalse(spread.is_debit_account_deprecated)
self.assertEqual(spread.journal_id, self.sales_journal)
self.assertEqual(spread.spread_type, "sale") self.assertEqual(spread.spread_type, "sale")
spread.invoice_type = "in_invoice" spread_form = Form(spread)
spread.onchange_invoice_type() spread_form.invoice_type = "in_invoice"
spread = spread_form.save()
self.assertEqual(spread.credit_account_id, self.account_payable) self.assertEqual(spread.credit_account_id, self.account_payable)
self.assertEqual(spread.journal_id.id, exp_journal) self.assertFalse(spread.is_credit_account_deprecated)
self.assertEqual(spread.journal_id, self.expenses_journal)
self.assertEqual(spread.spread_type, "purchase") self.assertEqual(spread.spread_type, "purchase")
def test_06_invoice_line_compute_spread_check(self):
invoice_account = self.account_receivable
invoice_line_account = self.account_expenses
invoice = self.env["account.invoice"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"account_id": invoice_account.id,
"type": "in_invoice",
}
)
invoice_line = self.env["account.invoice.line"].create(
{
"product_id": self.env.ref("product.product_product_4").id,
"quantity": 1.0,
"price_unit": 100.0,
"invoice_id": invoice.id,
"name": "product that cost 100",
"account_id": invoice_line_account.id,
}
)
invoice_line2 = invoice_line.copy()
self.assertFalse(invoice_line.spread_id)
self.assertEqual(invoice_line.spread_check, "unlinked")
spread = self.env["account.spread"].create(
{
"name": "test",
"invoice_type": "in_invoice",
"debit_account_id": self.debit_account.id,
"credit_account_id": self.credit_account.id,
}
)
invoice_line.spread_id = spread
self.assertTrue(invoice_line.spread_id)
self.assertEqual(invoice_line.spread_check, "linked")
self.assertFalse(invoice_line2.spread_id)
self.assertEqual(invoice_line2.spread_check, "unlinked")
invoice.action_invoice_open()
self.assertTrue(invoice_line.spread_id)
self.assertEqual(invoice_line.spread_check, "linked")
self.assertFalse(invoice_line2.spread_id)
self.assertEqual(invoice_line2.spread_check, "unavailable")
self.assertTrue(spread.display_create_all_moves)
self.assertTrue(spread.display_recompute_buttons)
self.assertTrue(spread.display_move_line_auto_post)
def test_07_create_spread_template(self): def test_07_create_spread_template(self):
account_revenue = self.account_revenue
account_payable = self.account_payable
spread_template = self.env["account.spread.template"].create( spread_template = self.env["account.spread.template"].create(
{ {
"name": "test", "name": "test",
"spread_type": "sale", "spread_type": "sale",
"spread_account_id": account_revenue.id, "spread_account_id": self.account_revenue.id,
} }
) )
my_company = self.env.user.company_id self.assertEqual(spread_template.company_id, self.env.company)
self.assertEqual(spread_template.company_id, my_company)
self.assertTrue(spread_template.spread_journal_id) self.assertTrue(spread_template.spread_journal_id)
exp_journal = self.ref("account_spread_cost_revenue.expenses_journal") self.env.company.default_spread_revenue_account_id = self.account_revenue
sales_journal = self.ref("account_spread_cost_revenue.sales_journal") self.env.company.default_spread_expense_account_id = self.account_payable
my_company.default_spread_revenue_account_id = account_revenue self.env.company.default_spread_revenue_journal_id = self.sales_journal
my_company.default_spread_expense_account_id = account_payable self.env.company.default_spread_expense_journal_id = self.expenses_journal
my_company.default_spread_revenue_journal_id = sales_journal
my_company.default_spread_expense_journal_id = exp_journal
spread_template.spread_type = "purchase" spread_template.spread_type = "purchase"
spread_template.onchange_spread_type()
self.assertTrue(spread_template.spread_journal_id) self.assertTrue(spread_template.spread_journal_id)
self.assertTrue(spread_template.spread_account_id) self.assertTrue(spread_template.spread_account_id)
self.assertEqual(spread_template.spread_account_id, account_payable) self.assertEqual(spread_template.spread_account_id, self.account_payable)
self.assertEqual(spread_template.spread_journal_id.id, exp_journal) self.assertEqual(spread_template.spread_journal_id, self.expenses_journal)
spread_vals = spread_template._prepare_spread_from_template() spread_vals = spread_template._prepare_spread_from_template()
self.assertTrue(spread_vals["name"]) self.assertTrue(spread_vals["name"])
@ -267,11 +207,10 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
self.assertTrue(spread_vals["credit_account_id"]) self.assertTrue(spread_vals["credit_account_id"])
spread_template.spread_type = "sale" spread_template.spread_type = "sale"
spread_template.onchange_spread_type()
self.assertTrue(spread_template.spread_journal_id) self.assertTrue(spread_template.spread_journal_id)
self.assertTrue(spread_template.spread_account_id) self.assertTrue(spread_template.spread_account_id)
self.assertEqual(spread_template.spread_account_id, account_revenue) self.assertEqual(spread_template.spread_account_id, self.account_revenue)
self.assertEqual(spread_template.spread_journal_id.id, sales_journal) self.assertEqual(spread_template.spread_journal_id, self.sales_journal)
spread_vals = spread_template._prepare_spread_from_template() spread_vals = spread_template._prepare_spread_from_template()
self.assertTrue(spread_vals["name"]) self.assertTrue(spread_vals["name"])
@ -282,12 +221,11 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
self.assertTrue(spread_vals["debit_account_id"]) self.assertTrue(spread_vals["debit_account_id"])
def test_08_check_template_invoice_type(self): def test_08_check_template_invoice_type(self):
account_revenue = self.account_revenue
template_sale = self.env["account.spread.template"].create( template_sale = self.env["account.spread.template"].create(
{ {
"name": "test", "name": "test",
"spread_type": "sale", "spread_type": "sale",
"spread_account_id": account_revenue.id, "spread_account_id": self.account_revenue.id,
} }
) )
template_purchase = self.env["account.spread.template"].create( template_purchase = self.env["account.spread.template"].create(
@ -305,78 +243,35 @@ class TestAccountSpreadCostRevenue(common.TransactionCase):
"credit_account_id": self.credit_account.id, "credit_account_id": self.credit_account.id,
} }
) )
spread.template_id = template_sale
self.assertEqual(spread.template_id, template_sale)
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
spread.template_id = template_purchase spread.template_id = template_purchase
self.assertEqual(spread.invoice_type, "out_invoice")
spread.onchange_template()
self.assertEqual(spread.invoice_type, "in_invoice")
spread.template_id = False
spread.invoice_type = "in_invoice"
spread.template_id = template_purchase
self.assertEqual(spread.template_id, template_purchase)
with self.assertRaises(ValidationError):
spread.template_id = template_sale
self.assertEqual(spread.invoice_type, "in_invoice")
spread.onchange_template()
self.assertEqual(spread.invoice_type, "out_invoice")
spread.template_id = False
spread.invoice_type = "out_invoice"
spread.template_id = template_sale spread.template_id = template_sale
self.assertEqual(spread.template_id, template_sale) self.assertEqual(spread.template_id, template_sale)
with self.assertRaises(ValidationError):
spread.invoice_type = "in_invoice"
self.assertEqual(spread.invoice_type, "in_invoice")
spread.onchange_template()
self.assertEqual(spread.invoice_type, "out_invoice")
spread.template_id = False
spread.invoice_type = "in_invoice"
spread.template_id = template_purchase
self.assertEqual(spread.template_id, template_purchase)
with self.assertRaises(ValidationError):
spread.invoice_type = "out_invoice"
self.assertEqual(spread.invoice_type, "out_invoice")
spread.onchange_template()
self.assertEqual(spread.invoice_type, "in_invoice")
self.assertFalse(spread.display_create_all_moves) self.assertFalse(spread.display_create_all_moves)
self.assertTrue(spread.display_recompute_buttons) self.assertTrue(spread.display_recompute_buttons)
self.assertTrue(spread.display_move_line_auto_post) self.assertTrue(spread.display_move_line_auto_post)
def test_09_wrong_invoice_type(self):
invoice_account = self.account_receivable
invoice_line_account = self.account_expenses
invoice = self.env["account.invoice"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"account_id": invoice_account.id,
"type": "in_invoice",
}
)
invoice_line = self.env["account.invoice.line"].create(
{
"product_id": self.env.ref("product.product_product_4").id,
"quantity": 1.0,
"price_unit": 100.0,
"invoice_id": invoice.id,
"name": "product that cost 100",
"account_id": invoice_line_account.id,
}
)
spread = self.env["account.spread"].create( spread = self.env["account.spread"].create(
{ {
"name": "test", "name": "test",
"invoice_type": "out_invoice", "invoice_type": "in_invoice",
"debit_account_id": self.debit_account.id, "debit_account_id": self.debit_account.id,
"credit_account_id": self.credit_account.id, "credit_account_id": self.credit_account.id,
} }
) )
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
invoice_line.spread_id = spread spread.template_id = template_sale
spread.template_id = template_purchase
self.assertEqual(spread.template_id, template_purchase)
self.assertFalse(spread.display_create_all_moves)
self.assertTrue(spread.display_recompute_buttons)
self.assertTrue(spread.display_move_line_auto_post)
def test_10_account_spread_unlink(self): def test_10_account_spread_unlink(self):
spread = self.env["account.spread"].create( spread = self.env["account.spread"].create(

View File

@ -1,4 +1,4 @@
# Copyright 2017-2019 Onestein (<https://www.onestein.eu>) # Copyright 2017-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import datetime import datetime
@ -113,7 +113,6 @@ class TestComputeSpreadBoard(common.TransactionCase):
line.create_move() line.create_move()
self.assertTrue(line.move_id) self.assertTrue(line.move_id)
self.spread.journal_id.update_posted = True
self.spread.action_recalculate_spread() self.spread.action_recalculate_spread()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
for line in spread_lines: for line in spread_lines:
@ -313,7 +312,6 @@ class TestComputeSpreadBoard(common.TransactionCase):
self.assertTrue(isinstance(attrs, dict)) self.assertTrue(isinstance(attrs, dict))
# post and then unlink all created moves # post and then unlink all created moves
self.spread.journal_id.write({"update_posted": True})
for line in self.spread.line_ids: for line in self.spread.line_ids:
line.move_id.post() line.move_id.post()
self.spread.line_ids.unlink_move() self.spread.line_ids.unlink_move()
@ -354,9 +352,7 @@ class TestComputeSpreadBoard(common.TransactionCase):
with self.assertRaises(UserError): with self.assertRaises(UserError):
line.create_move() line.create_move()
self.spread.write( self.spread.write({"move_line_auto_post": True})
{"move_line_auto_post": True,}
)
self.spread.action_recalculate_spread() self.spread.action_recalculate_spread()
for line in self.spread.line_ids: for line in self.spread.line_ids:
@ -379,9 +375,9 @@ class TestComputeSpreadBoard(common.TransactionCase):
self.spread.compute_spread_board() self.spread.compute_spread_board()
spread_lines = self.spread.line_ids spread_lines = self.spread.line_ids
self.assertEqual(len(spread_lines), 3) self.assertEqual(len(spread_lines), 3)
self.assertEqual(115.32, spread_lines[0].amount) self.assertAlmostEquals(115.32, spread_lines[0].amount)
self.assertEqual(115.32, spread_lines[1].amount) self.assertAlmostEquals(115.32, spread_lines[1].amount)
self.assertEqual(115.32, spread_lines[2].amount) self.assertAlmostEquals(115.32, spread_lines[2].amount)
self.assertEqual(datetime.date(2017, 1, 31), spread_lines[0].date) self.assertEqual(datetime.date(2017, 1, 31), spread_lines[0].date)
self.assertEqual(datetime.date(2017, 2, 28), spread_lines[1].date) self.assertEqual(datetime.date(2017, 2, 28), spread_lines[1].date)
self.assertEqual(datetime.date(2017, 3, 31), spread_lines[2].date) self.assertEqual(datetime.date(2017, 3, 31), spread_lines[2].date)
@ -427,7 +423,6 @@ class TestComputeSpreadBoard(common.TransactionCase):
action = line.open_move() action = line.open_move()
self.assertTrue(action) self.assertTrue(action)
self.spread.journal_id.update_posted = True
self.spread.line_ids.unlink_move() self.spread.line_ids.unlink_move()
for line in self.spread.line_ids: for line in self.spread.line_ids:
self.assertFalse(line.move_id) self.assertFalse(line.move_id)
@ -471,7 +466,7 @@ class TestComputeSpreadBoard(common.TransactionCase):
def test_12_supplier_invoice_auto_post(self): def test_12_supplier_invoice_auto_post(self):
# spread date set # spread date set
self.spread.write( self.spread.write(
{"period_number": 8, "period_type": "month", "move_line_auto_post": True,} {"period_number": 8, "period_type": "month", "move_line_auto_post": True}
) )
self.spread.compute_spread_board() self.spread.compute_spread_board()
@ -496,9 +491,7 @@ class TestComputeSpreadBoard(common.TransactionCase):
self.assertEqual(self.spread.unposted_amount, 0.0) self.assertEqual(self.spread.unposted_amount, 0.0)
def test_13_create_move_in_invoice_auto_post(self): def test_13_create_move_in_invoice_auto_post(self):
self.spread2.write( self.spread2.write({"period_number": 4, "move_line_auto_post": True})
{"period_number": 4, "move_line_auto_post": True,}
)
self.spread_account.reconcile = True self.spread_account.reconcile = True
self.assertTrue(self.spread_account.reconcile) self.assertTrue(self.spread_account.reconcile)

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_supplier_invoice_spread" model="ir.ui.view">
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_supplier_form" />
<field name="arch" type="xml">
<xpath
expr="//field[@name='invoice_line_ids']/tree/field[@name='quantity']"
position="before"
>
<field
name="spread_check"
widget="spread_line_widget"
groups="account.group_account_user,account.group_account_manager"
/>
</xpath>
</field>
</record>
<record id="view_customer_invoice_spread" model="ir.ui.view">
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form" />
<field name="arch" type="xml">
<xpath
expr="//field[@name='invoice_line_ids']/tree/field[@name='quantity']"
position="before"
>
<field
name="spread_check"
widget="spread_line_widget"
groups="account.group_account_user,account.group_account_manager"
/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_invoice_spread" model="ir.ui.view">
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form" />
<field name="arch" type="xml">
<xpath
expr="//field[@name='invoice_line_ids']/tree/field[@name='quantity']"
position="before"
>
<field
name="spread_check"
widget="spread_line_widget"
groups="account.group_account_user,account.group_account_manager"
/>
</xpath>
</field>
</record>
<record id="action_account_moves_all_spread" model="ir.actions.act_window">
<field
name="context"
>{'journal_type':'general', 'search_default_group_by_move': 0, 'search_default_posted':1, 'name_groupby':1}</field>
<field name="name">Journal Items</field>
<field name="res_model">account.move.line</field>
<field
name="domain"
>[('display_type', 'not in', ('line_section', 'line_note'))]</field>
<field name="view_id" ref="account.view_move_line_tree_grouped" />
<field name="view_mode">tree,pivot,graph,form,kanban</field>
</record>
</odoo>

View File

@ -35,7 +35,7 @@
/> />
</header> </header>
<sheet> <sheet>
<div class="oe_button_box"> <div name="button_box" class="oe_button_box">
<button <button
name="open_reconcile_view" name="open_reconcile_view"
class="oe_stat_button" class="oe_stat_button"
@ -44,20 +44,13 @@
string="Reconciled entries" string="Reconciled entries"
> >
</button> </button>
<button
name="toggle_active"
type="object"
attrs="{'invisible': [('all_posted','=',False)]}"
class="oe_stat_button"
icon="fa-archive"
>
<field
name="active"
widget="boolean_button"
options='{"terminology": "archive"}'
/>
</button>
</div> </div>
<widget
name="web_ribbon"
title="Archived"
bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}"
/>
<div class="oe_title"> <div class="oe_title">
<label for="name" string="Spread Board Name" /> <label for="name" string="Spread Board Name" />
<h1> <h1>
@ -77,6 +70,7 @@
<field name="display_recompute_buttons" invisible="1" /> <field name="display_recompute_buttons" invisible="1" />
<field name="display_move_line_auto_post" invisible="1" /> <field name="display_move_line_auto_post" invisible="1" />
<field name="all_posted" invisible="1" /> <field name="all_posted" invisible="1" />
<field name="active" invisible="1" />
<field name="use_invoice_line_account" invisible="1" /> <field name="use_invoice_line_account" invisible="1" />
</group> </group>
</group> </group>
@ -140,7 +134,7 @@
<field <field
name="debit_account_id" name="debit_account_id"
required="1" required="1"
domain="[('company_id', '=', company_id), ('deprecated', '=', False)]" domain="[('company_id', '=', company_id), ('deprecated', '=', False), ('internal_type','!=','liquidity'), ('internal_group', '!=', 'off_balance')]"
attrs="{'readonly':[('invoice_line_id','!=',False)]}" attrs="{'readonly':[('invoice_line_id','!=',False)]}"
/> />
<span <span
@ -210,7 +204,7 @@
<field <field
name="credit_account_id" name="credit_account_id"
required="1" required="1"
domain="[('company_id', '=', company_id), ('deprecated', '=', False)]" domain="[('company_id', '=', company_id), ('deprecated', '=', False), ('internal_type','!=','liquidity'), ('internal_group', '!=', 'off_balance')]"
attrs="{'readonly':[('invoice_line_id','!=',False)]}" attrs="{'readonly':[('invoice_line_id','!=',False)]}"
/> />
<span <span
@ -391,6 +385,7 @@
<field name="model">account.spread</field> <field name="model">account.spread</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="name" string="Spread" />
<filter <filter
string="Archived" string="Archived"
name="inactive" name="inactive"
@ -402,13 +397,12 @@
<record id="action_account_spread_form" model="ir.actions.act_window"> <record id="action_account_spread_form" model="ir.actions.act_window">
<field name="name">Spread Costs/Revenues</field> <field name="name">Spread Costs/Revenues</field>
<field name="res_model">account.spread</field> <field name="res_model">account.spread</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_spread_tree" /> <field name="view_id" ref="view_account_spread_tree" />
</record> </record>
<menuitem <menuitem
id="menu_action_account_spread_form" id="menu_action_account_spread_form"
parent="account.menu_finance_entries_accounting_entries" parent="account.menu_finance_entries_accounting_miscellaneous"
action="action_account_spread_form" action="action_account_spread_form"
groups="account.group_account_user,account.group_account_manager" groups="account.group_account_user,account.group_account_manager"
/> />

View File

@ -31,7 +31,7 @@
<field name="use_invoice_line_account" /> <field name="use_invoice_line_account" />
<field <field
name="spread_account_id" name="spread_account_id"
domain="[('company_id', '=', company_id), ('deprecated', '=', False)]" domain="[('company_id', '=', company_id), ('deprecated', '=', False), ('internal_type','!=','liquidity'), ('internal_group', '!=', 'off_balance')]"
options="{'no_create': True}" options="{'no_create': True}"
attrs="{'required': [('use_invoice_line_account', '!=', True)], 'invisible': [('use_invoice_line_account', '=', True)]}" attrs="{'required': [('use_invoice_line_account', '!=', True)], 'invisible': [('use_invoice_line_account', '=', True)]}"
/> />
@ -88,7 +88,6 @@
<record id="action_account_spread_template_form" model="ir.actions.act_window"> <record id="action_account_spread_template_form" model="ir.actions.act_window">
<field name="name">Spread Templates</field> <field name="name">Spread Templates</field>
<field name="res_model">account.spread.template</field> <field name="res_model">account.spread.template</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_spread_template_tree" /> <field name="view_id" ref="view_account_spread_template_tree" />
</record> </record>

View File

@ -32,7 +32,7 @@
</group> </group>
<group name="spreading_options_right"> <group name="spreading_options_right">
<field name="force_move_auto_post" /> <field name="force_move_auto_post" />
<field name="auto_archive" /> <field name="auto_archive_spread" />
</group> </group>
</group> </group>
</page> </page>

View File

@ -1,4 +1,4 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>) # Copyright 2018-2020 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models from odoo import _, api, fields, models
@ -27,13 +27,9 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
return "link" return "link"
invoice_line_id = fields.Many2one( invoice_line_id = fields.Many2one(
"account.invoice.line", "account.move.line", readonly=True, required=True, ondelete="cascade"
string="Invoice Line",
readonly=True,
required=True,
ondelete="cascade",
) )
invoice_id = fields.Many2one(related="invoice_line_id.invoice_id", readonly=True) invoice_id = fields.Many2one(related="invoice_line_id.move_id", readonly=True)
invoice_type = fields.Selection( invoice_type = fields.Selection(
[ [
("out_invoice", "Customer Invoice"), ("out_invoice", "Customer Invoice"),
@ -49,69 +45,89 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
compute="_compute_invoice_type", compute="_compute_invoice_type",
store=True, store=True,
) )
spread_id = fields.Many2one("account.spread", string="Spread Board") spread_invoice_type_domain_ids = fields.One2many(
company_id = fields.Many2one("res.company", string="Company", required=True) "account.spread", compute="_compute_spread_invoice_type_domain",
)
spread_id = fields.Many2one(
"account.spread",
string="Spread Board",
domain="[('id', 'in', spread_invoice_type_domain_ids)]",
)
company_id = fields.Many2one("res.company", required=True)
spread_action_type = fields.Selection( spread_action_type = fields.Selection(
selection=_selection_spread_action_type, selection=_selection_spread_action_type,
default=_selection_default_spread_action_type, default=_selection_default_spread_action_type,
) )
template_id = fields.Many2one("account.spread.template", string="Spread Template") template_id = fields.Many2one("account.spread.template", string="Spread Template")
use_invoice_line_account = fields.Boolean( use_invoice_line_account = fields.Boolean(
string="Use invoice line's account",
help="Use invoice line's account as Balance sheet / spread account.\n" help="Use invoice line's account as Balance sheet / spread account.\n"
"In this case, user need to select expense/revenue account too.", "In this case, user need to select expense/revenue account too.",
) )
spread_account_id = fields.Many2one( spread_account_id = fields.Many2one(
"account.account", string="Balance sheet account / Spread account", store=True "account.account",
string="Balance sheet account / Spread account",
compute="_compute_spread_account",
readonly=False,
store=True,
) )
exp_rev_account_id = fields.Many2one( exp_rev_account_id = fields.Many2one(
"account.account", "account.account",
string="Expense/revenue account", string="Expense/revenue account",
help="Optional account to overwrite the existing expense/revenue " "account", help="Optional account to overwrite the existing expense/revenue account",
) )
spread_journal_id = fields.Many2one( spread_journal_id = fields.Many2one(
"account.journal", string="Spread Journal", store=True "account.journal",
string="Spread Journal",
compute="_compute_spread_journal",
readonly=False,
store=True,
) )
@api.depends("invoice_line_id") @api.depends("invoice_line_id")
def _compute_invoice_type(self): def _compute_invoice_type(self):
for wizard in self: for wizard in self:
invoice = wizard.invoice_line_id.invoice_id invoice = wizard.invoice_line_id.move_id
wizard.invoice_type = invoice.type wizard.invoice_type = invoice.type
if invoice.type in ["out_invoice", "out_refund"]: if invoice.is_sale_document(include_receipts=True):
wizard.spread_type = "sale" wizard.spread_type = "sale"
else: else:
wizard.spread_type = "purchase" wizard.spread_type = "purchase"
@api.onchange("company_id", "invoice_type") @api.depends("invoice_type", "company_id")
def _onchange_spread_journal_account(self): def _compute_spread_journal(self):
for wizard in self: for wizard in self:
company = wizard.company_id journal_revenue = wizard.company_id.default_spread_revenue_journal_id
acc_revenue = company.default_spread_revenue_account_id journal_expense = wizard.company_id.default_spread_expense_journal_id
acc_expense = company.default_spread_expense_account_id
journal_revenue = company.default_spread_revenue_journal_id
journal_expense = company.default_spread_expense_journal_id
if wizard.invoice_type in ("out_invoice", "in_refund"): if wizard.invoice_type in ("out_invoice", "in_refund"):
wizard.spread_account_id = acc_revenue
wizard.spread_journal_id = journal_revenue wizard.spread_journal_id = journal_revenue
else: else:
wizard.spread_account_id = acc_expense
wizard.spread_journal_id = journal_expense wizard.spread_journal_id = journal_expense
@api.multi @api.depends("invoice_type", "company_id")
def _compute_spread_account(self):
for wizard in self:
acc_revenue = wizard.company_id.default_spread_revenue_account_id
acc_expense = wizard.company_id.default_spread_expense_account_id
if wizard.invoice_type in ("out_invoice", "in_refund"):
wizard.spread_account_id = acc_revenue
else:
wizard.spread_account_id = acc_expense
def _inverse_spread_journal_account(self): def _inverse_spread_journal_account(self):
"""Keep this for making the fields editable""" """Keep this for making the fields editable"""
pass pass
@api.onchange("invoice_type") @api.depends("company_id", "invoice_type")
def onchange_invoice_type(self): def _compute_spread_invoice_type_domain(self):
domain = [ for wizard in self:
("invoice_id", "=", False), spreads = self.env["account.spread"].search(
("invoice_type", "=", self.invoice_type), [
("company_id", "=", self.company_id.id), ("invoice_id", "=", False),
] ("invoice_type", "=", wizard.invoice_type),
res = {"domain": {"spread_id": domain}} ("company_id", "=", wizard.company_id.id),
return res ]
)
wizard.spread_invoice_type_domain_ids = spreads
@api.onchange("use_invoice_line_account") @api.onchange("use_invoice_line_account")
def _onchange_user_invoice_line_account(self): def _onchange_user_invoice_line_account(self):
@ -120,7 +136,6 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
) )
self.exp_rev_account_id = False self.exp_rev_account_id = False
@api.multi
def confirm(self): def confirm(self):
self.ensure_one() self.ensure_one()
@ -130,7 +145,6 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
return { return {
"name": _("Spread Details"), "name": _("Spread Details"),
"view_type": "form",
"view_mode": "form", "view_mode": "form",
"res_model": "account.spread", "res_model": "account.spread",
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -149,10 +163,9 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
self.exp_rev_account_id or self.invoice_line_id.account_id self.exp_rev_account_id or self.invoice_line_id.account_id
) )
analytic_account = self.invoice_line_id.account_analytic_id analytic_account = self.invoice_line_id.analytic_account_id
analytic_tags = self.invoice_line_id.analytic_tag_ids analytic_tags = self.invoice_line_id.analytic_tag_ids
date_invoice = self.invoice_id.date_invoice or fields.Date.today() date_invoice = self.invoice_id.invoice_date or fields.Date.today()
use_il_account = self.use_invoice_line_account
return { return {
"name": _("New Spread Board"), "name": _("New Spread Board"),
@ -166,7 +179,7 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
"default_name": self.invoice_line_id.name, "default_name": self.invoice_line_id.name,
"default_invoice_type": self.invoice_type, "default_invoice_type": self.invoice_type,
"default_invoice_line_id": self.invoice_line_id.id, "default_invoice_line_id": self.invoice_line_id.id,
"default_use_invoice_line_account": use_il_account, "default_use_invoice_line_account": self.use_invoice_line_account,
"default_debit_account_id": debit_account.id, "default_debit_account_id": debit_account.id,
"default_credit_account_id": credit_account.id, "default_credit_account_id": credit_account.id,
"default_journal_id": self.spread_journal_id.id, "default_journal_id": self.spread_journal_id.id,
@ -186,7 +199,7 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
spread_vals = self.template_id._prepare_spread_from_template( spread_vals = self.template_id._prepare_spread_from_template(
spread_account_id=spread_account_id spread_account_id=spread_account_id
) )
date_invoice = self.invoice_id.date_invoice date_invoice = self.invoice_id.invoice_date
date_invoice = date_invoice or self.template_id.start_date date_invoice = date_invoice or self.template_id.start_date
date_invoice = date_invoice or fields.Date.today() date_invoice = date_invoice or fields.Date.today()
spread_vals["spread_date"] = date_invoice spread_vals["spread_date"] = date_invoice
@ -201,9 +214,11 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
else: else:
spread_vals["debit_account_id"] = account.id spread_vals["debit_account_id"] = account.id
analytic_account = self.invoice_line_id.account_analytic_id analytic_account = self.invoice_line_id.analytic_account_id
spread_vals["account_analytic_id"] = analytic_account.id spread_vals["account_analytic_id"] = analytic_account.id
spread_vals["currency_id"] = self.invoice_id.currency_id.id
spread = self.env["account.spread"].create(spread_vals) spread = self.env["account.spread"].create(spread_vals)
analytic_tags = self.invoice_line_id.analytic_tag_ids analytic_tags = self.invoice_line_id.analytic_tag_ids
@ -212,7 +227,6 @@ class AccountSpreadInvoiceLineLinkWizard(models.TransientModel):
self.invoice_line_id.spread_id = spread self.invoice_line_id.spread_id = spread
return { return {
"name": _("Spread Details"), "name": _("Spread Details"),
"view_type": "form",
"view_mode": "form", "view_mode": "form",
"res_model": "account.spread", "res_model": "account.spread",
"type": "ir.actions.act_window", "type": "ir.actions.act_window",

View File

@ -37,7 +37,7 @@
<field <field
name="spread_account_id" name="spread_account_id"
attrs="{'invisible': [('spread_action_type', '!=', 'new')],'required': [('spread_action_type', '=', 'new')]}" attrs="{'invisible': [('spread_action_type', '!=', 'new')],'required': [('spread_action_type', '=', 'new')]}"
domain="[('company_id', '=', company_id), ('deprecated', '=', False)]" domain="[('company_id', '=', company_id), ('deprecated', '=', False), ('internal_type','!=','liquidity'), ('internal_group', '!=', 'off_balance')]"
options="{'no_create': True}" options="{'no_create': True}"
/> />
<field <field