from odoo import models, fields, api, _ from odoo.exceptions import UserError import datetime class StockPicking(models.Model): _inherit = 'stock.picking' def action_generate_asset_codes(self): self.ensure_one() for move in self.move_ids_without_package: # Only generate if product creates asset (optional check, but good practice) # However, user wants to manually decide, so we generate for all lines that look like assets? # Let's assume user triggers this button for asset receipts. # We can check if product has an asset model set in accounting? # Or just blindly generate if missing? # Re-using logic from account.asset.create: if not move.asset_code and move.product_id: sequence = self.env['ir.sequence'].next_by_code('ga.asset.code') or '00000' year = datetime.datetime.now().year # Logic from AccountAsset create: [Barcode or AST]/[Year]/[Sequence] prefix = move.product_id.barcode or 'AST' # Note: Sequence next_by_code returns '/0000X' as configured in XML move.asset_code = f"{prefix}/{year}{sequence}" has_asset_moves = fields.Boolean(compute='_compute_has_asset_moves', store=True) @api.depends('move_ids.product_id', 'picking_type_code') def _compute_has_asset_moves(self): for pick in self: has_asset = False if pick.picking_type_code == 'incoming': for move in pick.move_ids_without_package: # Check if product is configured to create assets # Logic: Check Product's Expense Account or Category's Expense Account account = move.product_id.property_account_expense_id or move.product_id.categ_id.property_account_expense_categ_id if account and account.create_asset != 'no': has_asset = True break pick.has_asset_moves = has_asset def button_validate(self): # Validation Check for pick in self: if pick.has_asset_moves: for move in pick.move_ids_without_package: account = move.product_id.property_account_expense_id or move.product_id.categ_id.property_account_expense_categ_id if account and account.create_asset != 'no' and not move.asset_code: raise UserError(_( "Asset Code is missing for product '%s'. \n" "Please click 'Generate Asset Codes' before validating.", move.product_id.name )) return super(StockPicking, self).button_validate() def _action_done(self): res = super(StockPicking, self)._action_done() # After validation, create assets for moves with asset_code for pick in self: for move in pick.move_ids_without_package: if move.asset_code and not move.asset_id: # Get Account to check configuration account = move.product_id.property_account_expense_id or move.product_id.categ_id.property_account_expense_categ_id # Prepare basic values vals_base = { 'name': move.product_id.name, 'product_id': move.product_id.id, 'acquisition_date': fields.Date.today(), 'company_id': move.company_id.id, 'state': 'draft', } # Add Model if configured on Account if account and account.asset_model: vals_base['model_id'] = account.asset_model.id qty = int(move.quantity) # CASE 1: Multiple Assets per Line if account and account.multiple_assets_per_line and qty > 1: # 1st Asset uses the pre-generated code vals = vals_base.copy() vals['asset_code'] = move.asset_code vals['original_value'] = move.price_unit # Per unit value asset = self.env['account.asset'].create(vals) move.asset_id = asset.id # Link first one to move # Create remaining assets for i in range(qty - 1): vals_extra = vals_base.copy() vals_extra['original_value'] = move.price_unit # Generate new code for extra assets sequence = self.env['ir.sequence'].next_by_code('ga.asset.code') or '00000' year = datetime.datetime.now().year prefix = move.product_id.barcode or 'AST' vals_extra['asset_code'] = f"{prefix}/{year}{sequence}" self.env['account.asset'].create(vals_extra) # CASE 2: Single Assset (default) else: vals = vals_base.copy() vals['asset_code'] = move.asset_code vals['original_value'] = move.price_unit * move.quantity # Total Value asset = self.env['account.asset'].create(vals) move.asset_id = asset.id return res