1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, time
from dateutil.relativedelta import relativedelta
from odoo import fields, models, _, api, Command
from odoo.exceptions import UserError
from odoo.tools import format_list
class MrpWipAccountingLine(models.TransientModel):
_name = 'mrp.account.wip.accounting.line'
_description = 'Account move line to be created when posting WIP account move'
account_id = fields.Many2one('account.account', "Account")
label = fields.Char("Label")
debit = fields.Monetary("Debit", compute='_compute_debit', store=True, readonly=False)
credit = fields.Monetary("Credit", compute='_compute_credit', store=True, readonly=False)
currency_id = fields.Many2one('res.currency', "Currency", default=lambda self: self.env.company.currency_id)
wip_accounting_id = fields.Many2one('mrp.account.wip.accounting', "WIP accounting wizard")
_sql_constraints = [
('check_debit_credit', 'CHECK ( debit = 0 OR credit = 0 )',
'A single line cannot be both credit and debit.')
]
@api.depends('credit')
def _compute_debit(self):
for record in self:
if not record.currency_id.is_zero(record.credit):
record.debit = 0
@api.depends('debit')
def _compute_credit(self):
for record in self:
if not record.currency_id.is_zero(record.debit):
record.credit = 0
class MrpWipAccounting(models.TransientModel):
_name = 'mrp.account.wip.accounting'
_description = 'Wizard to post Manufacturing WIP account move'
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
productions = self.env['mrp.production'].browse(self.env.context.get('active_ids'))
# ignore selected MOs that aren't a WIP
productions = productions.filtered(lambda mo: mo.state in ['progress', 'to_close', 'confirmed'])
if 'journal_id' in fields_list:
default = self.env['product.category']._fields['property_stock_journal'].get_company_dependent_fallback(self.env['product.category'])
if default:
res['journal_id'] = default.id
if 'reference' in fields_list:
res['reference'] = _("Manufacturing WIP - %(orders_list)s", orders_list=productions and format_list(self.env, productions.mapped('name')) or _("Manual Entry"))
if 'mo_ids' in fields_list:
res['mo_ids'] = [Command.set(productions.ids)]
return res
date = fields.Date("Date", default=fields.Datetime.now)
reversal_date = fields.Date(
"Reversal Date", compute="_compute_reversal_date", required=True,
store=True, readonly=False)
journal_id = fields.Many2one('account.journal', "Journal", required=True)
reference = fields.Char("Reference")
line_ids = fields.One2many(
'mrp.account.wip.accounting.line', 'wip_accounting_id', "WIP accounting lines",
compute="_compute_line_ids", store=True, readonly=False)
mo_ids = fields.Many2many('mrp.production')
def _get_overhead_account(self):
overhead_account = self.env.company.account_production_wip_overhead_account_id
if overhead_account:
return overhead_account.id
ProductCategory = self.env['product.category']
cop_acc = ProductCategory._fields['property_stock_account_production_cost_id'].get_company_dependent_fallback(ProductCategory)
if cop_acc:
return cop_acc.id
return ProductCategory._fields['property_stock_account_input_categ_id'].get_company_dependent_fallback(ProductCategory).id
def _get_line_vals(self, productions=False, date=False):
if not productions:
productions = self.env['mrp.production']
if not date:
date = datetime.now().replace(hour=23, minute=59, second=59)
compo_value = sum(
ml.quantity_product_uom * (ml.product_id.lot_valuated and ml.lot_id and ml.lot_id.standard_price or ml.product_id.standard_price)
for ml in productions.move_raw_ids.move_line_ids.filtered(lambda ml: ml.picked and ml.quantity and ml.date <= date)
)
overhead_value = productions.workorder_ids._cal_cost(date)
sval_acc = self.env['product.category']._fields['property_stock_valuation_account_id'].get_company_dependent_fallback(self.env['product.category']).id
return [
Command.create({
'label': _("WIP - Component Value"),
'credit': compo_value,
'account_id': sval_acc,
}),
Command.create({
'label': _("WIP - Overhead"),
'credit': overhead_value,
'account_id': self._get_overhead_account(),
}),
Command.create({
'label': _("Manufacturing WIP - %(orders_list)s", orders_list=productions and format_list(self.env, productions.mapped('name')) or _("Manual Entry")),
'debit': compo_value + overhead_value,
'account_id': self.env.company.account_production_wip_account_id.id,
})
]
@api.depends('date')
def _compute_reversal_date(self):
for wizard in self:
if not wizard.reversal_date or wizard.reversal_date <= wizard.date:
wizard.reversal_date = wizard.date + relativedelta(days=1)
else:
wizard.reversal_date = wizard.reversal_date
@api.depends('date')
def _compute_line_ids(self):
for wizard in self:
# don't update lines when manual (i.e. no applicable MOs) entry
if not wizard.line_ids or wizard.mo_ids:
wizard.line_ids = [Command.clear()] + wizard._get_line_vals(wizard.mo_ids, datetime.combine(wizard.date, time.max))
def confirm(self):
self.ensure_one()
if self.env.company.currency_id.compare_amounts(sum(self.line_ids.mapped('credit')), sum(self.line_ids.mapped('debit'))) != 0:
raise UserError(_("Please make sure the total credit amount equals the total debit amount."))
if self.reversal_date <= self.date:
raise UserError(_("Reversal date must be after the posting date."))
move = self.env['account.move'].sudo().create({
'journal_id': self.journal_id.id,
'wip_production_ids': self.mo_ids.ids,
'date': self.date,
'ref': self.reference,
'move_type': 'entry',
'line_ids': [
Command.create({
'name': line.label,
'account_id': line.account_id.id,
'debit': line.debit,
'credit': line.credit,
}) for line in self.line_ids
]
})
move._post()
move._reverse_moves(default_values_list=[{
'ref': _("Reversal of: %s", self.reference),
'wip_production_ids': self.mo_ids.ids,
'date': self.reversal_date,
}])._post()
|