From 5ec64ef13197b4760479777ac4399a4de607bd07 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Fri, 6 Feb 2026 15:12:00 +0700 Subject: [PATCH] feat: Implement asset transfer functionality with a new transfer log, `in_transit` status for assets, and make depreciation fields readonly. --- __manifest__.py | 3 +- models/__init__.py | 1 + models/__pycache__/__init__.cpython-312.pyc | Bin 368 -> 413 bytes .../__pycache__/account_asset.cpython-312.pyc | Bin 2306 -> 4216 bytes models/account_asset.py | 32 +++++ models/ga_asset_transfer_log.py | 119 ++++++++++++++++++ security/ga_asset_security.xml | 6 + security/ir.model.access.csv | 2 + views/account_asset_views.xml | 80 +++++++++--- views/asset_transfer_log_views.xml | 83 ++++++++++++ .../asset_transfer_wizard.cpython-312.pyc | Bin 5099 -> 2554 bytes wizard/asset_transfer_wizard.py | 93 +++----------- 12 files changed, 327 insertions(+), 92 deletions(-) create mode 100644 models/ga_asset_transfer_log.py create mode 100644 views/asset_transfer_log_views.xml diff --git a/__manifest__.py b/__manifest__.py index 1edb029..e319fe1 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -11,7 +11,7 @@ - Asset Transfer Wizard """, 'author': 'Antigravity', - 'depends': ['base', 'account_asset', 'asset_code_field', 'product', 'stock', 'purchase'], + 'depends': ['base', 'account_asset', 'asset_code_field', 'product', 'stock', 'purchase', 'hr'], 'data': [ 'security/ga_asset_security.xml', 'security/ir.model.access.csv', @@ -19,6 +19,7 @@ 'views/account_asset_views.xml', 'views/stock_picking_views.xml', 'views/product_template_views.xml', + 'views/asset_transfer_log_views.xml', 'wizard/asset_transfer_views.xml', ], 'installable': True, diff --git a/models/__init__.py b/models/__init__.py index 521c357..fdde4c3 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -3,3 +3,4 @@ from . import stock_move from . import stock_picking from . import account_move from . import product_template +from . import ga_asset_transfer_log diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc index 389667ef5203e1becab664222c6a9ac88be531f0..21ba3358c8433b881d5b5eed74ac9e9c1c700f6e 100644 GIT binary patch delta 149 zcmeysG?$t8G%qg~0}#lkv}UfK$ScX{Gf~}9AcY}?Er%hOEsBkip^{ybV`5IEr6%Jo z(e%Xl#Ny)AlK7IM#Ju9P)S~#D{B%D}_FD`^%s@kmSU?0Th+qQ|>>z?;;)5(rULcDR hh>OL6#0O?ZM#j4g(svmoz@*e&2Dy6-%0=8jIRMX@BHI7} delta 84 zcmbQs{DF!0G%qg~0}xz4)s)#ikynz@VWPStOC_5o`^1V!WhTZ?7ov;ggQmO=#8upCKFez!+B~Hng zTfDVyB86yu73)tQaY|jN`LIVVl%=F z_Gmhil?hxv0<}1jr*v6WGF6(c#y^iF)woFem>(@snSD1p4#d^el&H)KvUK23=)eim zY;eN2!YnE$dSZ{HrbJoElux1vOid-#SqL$IrbK0Y=atYkBr2M$pUw1IYKLSk6i*`r z6+Jl{8iPcwkWNL$Az?~{qfbMqlvb0W774rTwY(A>7t=`{bCWPR6)$bMb4CC{8E>q0#{=;pO_t8}n7{#n<$ zUEZuWvCU;c72EU4dY(r>;@hf8?^{at;t^sVvLh0jQkx%jmk2@AD1-kc;+D zfF1l>?&zU0Uln(-qGCrYSgD+jIT&+%mM1v>-C?atM^5u+`9$@GHyny+T1hS0VSd$? z`6PP2{gL@qyUh1*nP30*Y`U)G-(`OFmigO+88W|mm-#j8^P7nMfp^zW?0uI|=#Hah;#ntH8$o6Bz zw+$4ZNPNNpz1^9uf8bDA@by)4F4p4|NdbEdKxNMtNp@^_&45`x^#NIIxQCA^)3s8_Y&9rHu4LyaJB@St$y%25o z(T46gc57+k1LUQ&CypRlB<#bS29x8sjJQ@4l0-kuQ4-O6k9lXC5DAfF5^=(~JcGSX z-ga9XfXf6So+M5a1dSG1A?KTab3Hn#reIV{PeLTkMz7404N|D~QB_jaBfZghnxyuW zC`qcKMJL3P>r=#I6EH;*Z`7{EqK?0u&Wq6Wdu*X z^sfZ3{208EzjhA8I6Zl8Y9KVZlkd~$E`NC<~)C`YcX5*Kc0Lz znLp5Hw0=}<9W+`8U+!IL9nGCH8(SB?_~r|9U&nWCPudneYllHNx-|3s?HBbgJTGQm zB=Wx;%5k}yM)m$R52y|;9X0$1z8}dS88QwHzl{HQ=)74U$Tx-a^&isl`uj6q&Mf-= z?Ei%sXnxE;#90&j|D_*DoiGkIt_IF67*}F5npTaUs}gh9f^TKW$#*7Oy|7 zat3}?2p%;LA1fX{Z5%#Lh6E$f`5t5W_LGI+Df9SX@%U%P@y`~yd`mA`ebx41zP+aq z>@~alirwdp?(@a&QKNg5%0!L8;Szej{X`*n((FG~?Elp0|Fqa2Gx}pxrpE~MuFIS$ z1kako=ZeD<#_&XOIAIJYsLV+taLUngv=BUEc6JpzPZ*siik*W-=OD4;rV;pX9p(66 zf&(ubibFRGLpRO76UDx(M&DJ6b95zeY;BIAN8|M%3#uD_f0Oz?`l4*b-=F9EUy(xu z;+tzR8ey!u?G7PG)dse!)SN1aR8b5q_a#npZ(l7CE~)?C{pg@ z>PE!d>u^eiuN5Wj;yUcXoFbB-#97g@3_(pO-Tyl>oVIA}(M3d~nm4X0I*DoIIprfU zF^!<0Di9qc7GvM0@|-~t!qTXxL#T(225oZ$mBdoP(Vo~vwa;{Hx$wJ4l5!eoCrL98 zOupj2|4V;?Z(6NyH|r0YP2FZwo4F^r+TQWqxhLn$NY@YQQ?(cwG$MmPxm|&(pDKOT zzBS-0^Pye>L7p?bAW+i<0s91MXgW#h3PHG?7L%nBUXT?M=42hwoyGhm;D5LUe|X zR+a*B943>35N6OZ!nK?`XgSx4{Z>O&)V?9j&v$|8Z=L^mwI-C?v(WaSEx)g8`J+P3 znfVK5ZAw)S~P#rbht~M%f9?YrBJI9 zW=$JGYkknVlwJO04S);GY4$Y$wIl5Og;h6SbO#N0@D)46xR1Yb0Z;RqMgHe3Tj92S zZYde|VvgnsL@%D@Rm%y8)*aY=iN4Y;xu?L|C(Fb){D8ebC(&<+7(G~8h%{?1hGG5z d+6~bDH{gHG#Tdqy>sc6hFz|2kc4zEW{|^?&^LGFM delta 660 zcmYjOO=uHA6rM?Tlif{rQ_`OtQf)}%A+%UjkWxczYsDYbi-Hg+YuugU%I3$JU8$vl zlpcC$rE!#a@-8_B(VG`f3Tg#W#)FgROa_c?>jn*{@sW*q{7EDb>)|FO214=PFx(-V54X_ML`el$;SbWa1 zYJSz5BLsOX^gTRD--bq#Papt~U=ysu1+e%GfV&}jKa!$}uqt7=IV^(qk?;hZ+Loa-rnLu!uT+izKYJP3J><+R6a((E5++fCVm}Z9OW#FSQwQ(lvkNV zkh{dx+hv!Su0uQ~35X3*r4V$>M^y{qRM1q%XvtY-(K@c#zU491G%dGGh-ng)7toig zJ(K2OW$^z~5BFz@HzcpQ>)iw2h7SA$S#=(6EPNhJHK!ZXk1u?riyGBG3}ic}p-AnX zh3#70fnzM>*i5r(m3p~)$8_vG#G^t>oSVVpw5OkkN9if!OeDvdyg|2(A~fhbW8?~! zIm42bnvGnuzlK@yUC+T{O?*sb_5C9({eMCR%+FK6;aF*|5~3b0r(5ZzM>d@0{WMLf$n jaSA!eT_+47{0eeAAom3%cBL7pZ69wI8iikg!=V2!cbTs$ diff --git a/models/account_asset.py b/models/account_asset.py index ad7c410..8e3184f 100644 --- a/models/account_asset.py +++ b/models/account_asset.py @@ -7,6 +7,10 @@ class AccountAsset(models.Model): product_id = fields.Many2one('product.product', string='Product') description = fields.Text(string='Description') + location_id = fields.Many2one('stock.location', string='Location', domain="[('company_id', '=', company_id)]") + employee_id = fields.Many2one('hr.employee', string='Employee', domain="[('company_id', '=', company_id)]") + in_transit = fields.Boolean(string='In Transit', default=False, help="Asset is currently being transferred between companies.") + @api.model_create_multi def create(self, vals_list): for vals in vals_list: @@ -45,6 +49,34 @@ class AccountAsset(models.Model): vals['asset_code'] = f"{prefix}/{year}{sequence}" + # Remove original_value if it is 0.0 to allow computation from lines later + if 'original_value' in vals and vals['original_value'] == 0.0: + del vals['original_value'] + + # Populate accounting fields from Asset Model if not already set + if vals.get('model_id'): + model = self.env['account.asset'].browse(vals['model_id']) + if model: + if not vals.get('method'): + vals['method'] = model.method + if not vals.get('method_number'): + vals['method_number'] = model.method_number + if not vals.get('method_period'): + vals['method_period'] = model.method_period + if not vals.get('prorata_computation_type'): + vals['prorata_computation_type'] = model.prorata_computation_type + + if not vals.get('account_asset_id'): + vals['account_asset_id'] = model.account_asset_id.id + if not vals.get('account_depreciation_id'): + vals['account_depreciation_id'] = model.account_depreciation_id.id + if not vals.get('account_depreciation_expense_id'): + vals['account_depreciation_expense_id'] = model.account_depreciation_expense_id.id + if not vals.get('journal_id'): + vals['journal_id'] = model.journal_id.id + if not vals.get('analytic_distribution') and model.analytic_distribution: + vals['analytic_distribution'] = model.analytic_distribution + return super(AccountAsset, self).create(vals_list) def action_open_transfer_wizard(self): diff --git a/models/ga_asset_transfer_log.py b/models/ga_asset_transfer_log.py new file mode 100644 index 0000000..43eab9a --- /dev/null +++ b/models/ga_asset_transfer_log.py @@ -0,0 +1,119 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + +class GaAssetTransferLog(models.Model): + _name = 'ga.asset.transfer.log' + _description = 'Asset Transfer Log' + _order = 'create_date desc' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='Transfer Reference', required=True, copy=False, readonly=True, default=lambda self: _('New')) + asset_id = fields.Many2one('account.asset', string='Asset', required=True, readonly=True, help="Asset being transferred.") + source_company_id = fields.Many2one('res.company', string='Source Company', required=True, readonly=True) + target_company_id = fields.Many2one('res.company', string='Target Company', required=True, readonly=True) + user_id = fields.Many2one('res.users', string='Requested By', required=True, readonly=True, default=lambda self: self.env.user) + transfer_date = fields.Date(string='Request Date', default=fields.Date.context_today, readonly=True) + + note = fields.Text(string='Transfer Note', readonly=True) + + state = fields.Selection([ + ('draft', 'Draft'), + ('transit', 'In Transit'), + ('done', 'Done'), + ('cancel', 'Cancelled') + ], string='Status', default='draft', tracking=True, copy=False) + + @api.model + def create(self, vals): + if vals.get('name', _('New')) == _('New'): + vals['name'] = self.env['ir.sequence'].next_by_code('ga.asset.transfer.log') or _('New') + return super(GaAssetTransferLog, self).create(vals) + + def action_validate(self): + self.ensure_one() + if self.state != 'transit': + raise UserError(_("You can only validate transfers that are currently In Transit.")) + + # Check if user is allowed to validate (must be in target company or have access) + # Assuming if they can see the button and access the record rules allow it, but let's double check company context + if self.env.company != self.target_company_id: + raise UserError(_("You must be logged into the Target Company (%s) to validate this transfer.", self.target_company_id.name)) + + # Perform the logic that was previously in the wizard + old_asset = self.asset_id + target_company = self.target_company_id + + # Logic adapted from original wizard + if old_asset.state in ('draft', 'model'): + old_asset.sudo().write({'company_id': target_company.id}) + old_asset.sudo().message_post(body=f"Asset transfer validated by {self.env.user.name}. Received in {target_company.name}.") + else: + # Running Asset -> Close and Clone + vals = { + 'name': old_asset.name, + 'product_id': old_asset.product_id.id, + 'asset_code': old_asset.asset_code, + 'original_value': old_asset.original_value, + 'already_depreciated_amount_import': old_asset.original_value - old_asset.value_residual, + 'acquisition_date': old_asset.acquisition_date, + 'prorata_date': old_asset.prorata_date, + 'method': old_asset.method, + 'method_number': old_asset.method_number, + 'method_period': old_asset.method_period, + 'method_progress_factor': old_asset.method_progress_factor, + 'prorata_computation_type': old_asset.prorata_computation_type, + 'company_id': target_company.id, + 'location_id': old_asset.location_id.id, # Copy location? Maybe not if it's new company. But user surely wants to update it later. + 'employee_id': old_asset.employee_id.id, # Keep employee if moving with them? + 'state': 'draft', + } + + # Find Asset Model in Target Company + if old_asset.product_id: + product = old_asset.product_id.with_company(target_company) + account = product.property_account_expense_id or product.categ_id.property_account_expense_categ_id + + if account and account.asset_model: + model = account.asset_model + vals['model_id'] = model.id + vals['account_asset_id'] = model.account_asset_id.id + vals['account_depreciation_id'] = model.account_depreciation_id.id + vals['account_depreciation_expense_id'] = model.account_depreciation_expense_id.id + vals['journal_id'] = model.journal_id.id + + # Close Old Asset + old_asset.depreciation_move_ids.filtered(lambda m: m.state == 'draft').with_context(force_delete=True).sudo().unlink() + old_asset.sudo().write({ + 'state': 'close', + 'active': False, + 'in_transit': False # No longer in transit + }) + old_asset.sudo().message_post(body=f"Asset transfer validated. Closed and moved to {target_company.name}.") + + # Create New Asset + new_asset = self.env['account.asset'].sudo().create(vals) + new_asset.sudo().message_post(body=f"Asset received from {self.source_company_id.name}. Transfer Ref: {self.name}") + + self.write({'state': 'done'}) + + # Unlock old asset just in case (if it was simple move) + if old_asset.id: # If simple move, old_asset is still the active one + old_asset.sudo().write({'in_transit': False}) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _('Success'), + 'message': _('Asset has been successfully transferred.'), + 'sticky': False, + } + } + + def action_cancel(self): + self.ensure_one() + if self.state == 'done': + raise UserError(_("You cannot cancel a completed transfer.")) + + self.asset_id.sudo().write({'in_transit': False}) + self.write({'state': 'cancel'}) diff --git a/security/ga_asset_security.xml b/security/ga_asset_security.xml index 394ec80..af4a7fa 100644 --- a/security/ga_asset_security.xml +++ b/security/ga_asset_security.xml @@ -20,4 +20,10 @@ + + Asset Transfer Log Multi Company + + ['|', ('source_company_id', 'in', company_ids), ('target_company_id', 'in', company_ids)] + + diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 7dffc28..3f96d43 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1,3 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_ga_asset_transfer_wizard_user,ga.asset.transfer.wizard.user,model_ga_asset_transfer_wizard,group_ga_asset_user,1,1,1,0 access_ga_asset_transfer_wizard_manager,ga.asset.transfer.wizard.manager,model_ga_asset_transfer_wizard,group_ga_asset_manager,1,1,1,1 +access_ga_asset_transfer_log_user,ga.asset.transfer.log.user,model_ga_asset_transfer_log,group_ga_asset_user,1,1,1,0 +access_ga_asset_transfer_log_manager,ga.asset.transfer.log.manager,model_ga_asset_transfer_log,group_ga_asset_manager,1,1,1,1 diff --git a/views/account_asset_views.xml b/views/account_asset_views.xml index 7dc5434..e3df2f1 100644 --- a/views/account_asset_views.xml +++ b/views/account_asset_views.xml @@ -26,7 +26,7 @@
-
@@ -47,8 +47,23 @@ + + + + + + + @@ -67,25 +82,15 @@ Assets account.asset tree,form - + [('state', '!=', 'model')] {'default_state': 'draft'} - - - tree - - - - - - - form - - - + @@ -96,4 +101,49 @@ action="action_ga_asset_management" sequence="1"/> + + + account.asset.form.inherit.ga + account.asset + + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + + diff --git a/views/asset_transfer_log_views.xml b/views/asset_transfer_log_views.xml new file mode 100644 index 0000000..2d42699 --- /dev/null +++ b/views/asset_transfer_log_views.xml @@ -0,0 +1,83 @@ + + + + ga.asset.transfer.log.tree + ga.asset.transfer.log + + + + + + + + + + + + + + + ga.asset.transfer.log.form + ga.asset.transfer.log + + +
+
+ +
+

+ +

+
+ + + + + + + + + + + + +
+
+ + + +
+ +
+
+ + + Pending Asset Receipts + ga.asset.transfer.log + tree,form + [('state', '=', 'transit')] + {'search_default_transit': 1} + +

+ No pending asset transfers found +

+ When an asset is transferred from another company, it will appear here for validation. +

+
+
+ + + + + +
diff --git a/wizard/__pycache__/asset_transfer_wizard.cpython-312.pyc b/wizard/__pycache__/asset_transfer_wizard.cpython-312.pyc index 318a4ea105da1c76ec99b00ba0e3de8f3686957a..71096feda99ed4be77f65ee0d90b9d95c53411d0 100644 GIT binary patch delta 1239 zcmZ8h&1)M+6yKTE>dPzH`m*Clu{X74SKtUtR69hjLrI&Yb`>X$Av7(Bjb@~dE$u3^ ztE3hwIu+VepnIvo#WbguUgE!?6nZEW%5Ga&v6tr1TYHdkN+9&m8QDrj7k1vq@Avli zcyF0M=U*hEKSd&8fjxK7C*%kJLG&>IZR-Hw^(xzD#4-%=$Lg6j$2>B(~v&5zJudI1ST0_D`egDB}8E>Y@H1xfXpN7F-*a& zWA|ri;C>!RB@sm8p7iaB(#PQ8pe|+|1k+YHluY=LM8JRngqMtzbx%qFKkou=ja%P~ zDJk#}Fz;q)A`$$r0EJK(jUai5*ok~8q9_{O6Vce7cuPPr6er-8kV{-|r)p|mH4JR# z&6Anr>w3)#^dn;CBrn*9%LI8bLvPUvE?4wMQ`L6*DO062Y!0V<8k?yn8K%lim}q6Q z!@o?-&35JuuA~f<6rvH6Fd9-!U1?9}mD?&yXf>s(Q{}!|CrCAkuH}_?arL5-1D@}$ zj&|BHLi1{cBSyYbCs;GRBqBz$uI`kXIH{6KzpfoW-gAd7`t5;KJN~~WxO7?}hN9^Y zlqS{?Z(vCLN>217lbChv1sd2e)Ef5uhDj>hI}GueJ5B6~rdsp-O_iz*gYw^^ogV7Z zN=ITH*$ZRMXi;3&HB84C$BS25l(O!Jrzc|VzUQw{%*H~aoCOJ@G4A+`7ScWG0VQm( z@<}V@rf!&?Pg5H>2N?y|tDZagEv*2VFy4jqYU2(E2ZuXLsz0?njPPXV~SDox?_f%#- zv>WP9zH&I?e+xv91b=4q(|3AtArgC3`mE&2Q;t0KtE{;4oFmVDEk8l_eDODVr7Mp; zDs?!4=k5I3j|F#Kb=FmPz3Qx2?X_A@5IzJ~Av4$DCVbA#d+_ksu;f*Ehhyj7hqgTT zOb{X;z@A^2T~Da+%HnSXn&a3!KLUe; z-xUV2{)rRmw6sif)^=j)+9F3^l3xT@8ais#@hUAafZqWISLulWfZv5#N0@!)n*qyT RTyZmtPG<3Mfd&2Ye*svWdX4}9 literal 5099 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 A&j0`b diff --git a/wizard/asset_transfer_wizard.py b/wizard/asset_transfer_wizard.py index 46fd2b0..fcc6952 100644 --- a/wizard/asset_transfer_wizard.py +++ b/wizard/asset_transfer_wizard.py @@ -12,86 +12,27 @@ class GaAssetTransferWizard(models.TransientModel): def action_transfer(self): self.ensure_one() if self.target_company_id and self.target_company_id != self.current_company_id: - old_asset = self.asset_id - - # CASE 1: Asset is Draft or Model -> Simple Move - # No depreciation history to preserve, so we just move it. - if old_asset.state in ('draft', 'model'): - old_asset.sudo().write({'company_id': self.target_company_id.id}) - if self.note: - old_asset.sudo().message_post(body=f"Asset transferred to {self.target_company_id.name}. Note: {self.note}") - - return { - 'name': 'Transferred Asset', - 'type': 'ir.actions.act_window', - 'res_model': 'account.asset', - 'view_mode': 'form', - 'res_id': old_asset.id, - 'target': 'current', - } - - # CASE 2: Asset is Running (Open/Parsed) -> Close and Clone - # We want to stop the old one and start fresh in new company - vals = { - 'name': old_asset.name, - 'product_id': old_asset.product_id.id, - 'asset_code': old_asset.asset_code, - 'original_value': old_asset.original_value, - 'already_depreciated_amount_import': old_asset.original_value - old_asset.value_residual, - 'acquisition_date': old_asset.acquisition_date, - 'prorata_date': old_asset.prorata_date, - 'method': old_asset.method, - 'method_number': old_asset.method_number, - 'method_period': old_asset.method_period, - 'method_progress_factor': old_asset.method_progress_factor, - 'prorata_computation_type': old_asset.prorata_computation_type, - 'company_id': self.target_company_id.id, - 'state': 'draft', - } - - # Find Asset Model in Target Company to apply Accounts - if old_asset.product_id: - # Get Product with company context - product = old_asset.product_id.with_company(self.target_company_id) - account = product.property_account_expense_id or product.categ_id.property_account_expense_categ_id - - if account and account.asset_model: - model = account.asset_model - vals['model_id'] = model.id - # Auto-populate Accounts from Model - vals['account_asset_id'] = model.account_asset_id.id - vals['account_depreciation_id'] = model.account_depreciation_id.id - vals['account_depreciation_expense_id'] = model.account_depreciation_expense_id.id - vals['journal_id'] = model.journal_id.id - - # 2. Close the old asset WITHOUT generating Disposal Moves - # User request: "no need to use dispose method (gain/loss account)" - # We simply stop future depreciation and archive the old asset. - # NOTE: The Asset Value remains on the old company's Balance Sheet until manually adjusted/transferred. - - # Cancel future draft moves (using sudo to bypass company rules if needed) - old_asset.depreciation_move_ids.filtered(lambda m: m.state == 'draft').with_context(force_delete=True).sudo().unlink() - - # Close and Archive (using sudo to ensure we can modify the record even if we are switching context) - old_asset.sudo().write({ - 'state': 'close', - 'active': False, + # Create Transfer Log + transfer_log = self.env['ga.asset.transfer.log'].create({ + 'asset_id': self.asset_id.id, + 'source_company_id': self.current_company_id.id, + 'target_company_id': self.target_company_id.id, + 'note': self.note, + 'state': 'transit', }) - old_asset.sudo().message_post(body=f"Asset transferred to {self.target_company_id.name}. Auto-disposal skipped.") + # Lock Asset + self.asset_id.sudo().write({'in_transit': True}) + self.asset_id.message_post(body=f"Asset transfer initiated to {self.target_company_id.name}. Waiting for validation. Ref: {transfer_log.name}") - # 3. Create the new asset in target company (using sudo to create in a company we might not be active in) - new_asset = self.env['account.asset'].sudo().create(vals) - new_asset.sudo().message_post(body=f"Asset transferred from {self.current_company_id.name}. Note: {self.note}") - - # 4. Open the new asset return { - 'name': 'Transferred Asset', - 'type': 'ir.actions.act_window', - 'res_model': 'account.asset', - 'view_mode': 'form', - 'res_id': new_asset.id, - 'target': 'current', + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Transfer Initiated', + 'message': f"Asset transfer to {self.target_company_id.name} is now pending validation.", + 'sticky': False, + } } return {'type': 'ir.actions.act_window_close'}