# -*- coding: utf-8 -*- from odoo import models, _, api from odoo.exceptions import ValidationError class PurchaseOrder(models.Model): _inherit = 'purchase.order' def action_budget(self): confirmation_lines = [] amount = 0 if self.amount_total <= 0: raise ValidationError(_("Total Amount MUST be greater than 0 !!!")) analytic_account = False if self.purchase_cost == 'default': analytic_account = self.env.user.company_id.purchase_analytic_account elif self.purchase_cost == 'department': analytic_account = self.department_id.analytic_account_id elif self.purchase_cost == 'project': analytic_account = self.project_id.analytic_account_id if not analytic_account: raise ValidationError(_("No analytic account for the project")) elif self.purchase_cost == 'product_line': pass else: raise ValidationError(_("No analytic account for the purchase")) for order in self: for rec in order.order_line: if rec.choosen: if self.purchase_cost == 'product_line': analytic_account = rec.account_analytic_id if not analytic_account: raise ValidationError( _("Please put cost center to the product line") + ': {}'.format(rec.product_id.name) ) account_id = rec.product_id.property_account_expense_id or rec.product_id.categ_id.property_account_expense_categ_id if not account_id: raise ValidationError( _("This product has no expense account") + ': {}'.format(rec.product_id.name) ) if not analytic_account: raise ValidationError(_("Analytic account not set")) budget_lines = self.env['crossovered.budget.lines'].search([ ('analytic_account_id', '=', analytic_account.id), ('crossovered_budget_id.state', '=', 'done'), ('crossovered_budget_id.date_from', '<=', self.date_order), ('crossovered_budget_id.date_to', '>=', self.date_order) ]) if not budget_lines: raise ValidationError( _("No approved budget found for analytic account: %s on date: %s") % (analytic_account.name, self.date_order) ) valid_budget_positions = budget_lines.mapped('general_budget_id').filtered( lambda x: account_id in x.account_ids ) if not valid_budget_positions: raise ValidationError( _("No budget position found that contains:\n" "- Expense Account: %s\n" "- For Analytic Account: %s\n" "- Product: %s") % (account_id.name, analytic_account.name, rec.product_id.name) ) final_budget_line = False remaining_amount = rec.price_subtotal for budget_position in valid_budget_positions: candidate_lines = budget_lines.filtered( lambda x: x.general_budget_id.id == budget_position.id ) for line in candidate_lines: line_remain = abs(line.remain) if line_remain <= 0: continue used_amount = min(line_remain, remaining_amount) confirmation_lines.append((0, 0, { 'amount': used_amount, 'analytic_account_id': analytic_account.id, 'description': f"{rec.product_id.name} ({budget_position.name})", 'budget_line_id': line.id, 'remain': line_remain, 'new_balance': line_remain - used_amount, 'account_id': account_id.id })) remaining_amount -= used_amount amount += used_amount final_budget_line = line if remaining_amount <= 0: break if remaining_amount <= 0: break if remaining_amount > 0: raise ValidationError(_( "Not enough budget available for analytic account '%s' (Expense account: %s).\n" "Remaining unallocated amount: %.2f" ) % (analytic_account.name, account_id.name, remaining_amount)) if final_budget_line: self.budget_id = final_budget_line.crossovered_budget_id.id data = { 'name': self.name, 'date': self.date_order, 'beneficiary_id': self.partner_id.id, 'department_id': self.department_id.id, 'type': 'purchase.order', 'ref': self.name, 'description': self.purpose, 'total_amount': self.amount_untaxed, 'lines_ids': confirmation_lines, 'po_id': self.id } self.env['budget.confirmation'].with_context({}).create(data) self.write({'state': 'waiting'})