change the strategy to readjust with chunking method and add resume feature
This commit is contained in:
parent
7296290bdc
commit
c9b08f37b4
@ -1,6 +1,10 @@
|
|||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
from odoo.tools import float_is_zero
|
from odoo.tools import float_is_zero
|
||||||
|
from collections import defaultdict
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class StockReadjustValuation(models.Model):
|
class StockReadjustValuation(models.Model):
|
||||||
_name = 'stock.readjust.valuation'
|
_name = 'stock.readjust.valuation'
|
||||||
@ -17,6 +21,7 @@ class StockReadjustValuation(models.Model):
|
|||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('draft', 'Draft'),
|
('draft', 'Draft'),
|
||||||
('calculated', 'Calculated'),
|
('calculated', 'Calculated'),
|
||||||
|
('processing', 'Processing'),
|
||||||
('done', 'Done'),
|
('done', 'Done'),
|
||||||
('cancel', 'Cancelled')
|
('cancel', 'Cancelled')
|
||||||
], string='Status', default='draft', tracking=True)
|
], string='Status', default='draft', tracking=True)
|
||||||
@ -249,6 +254,542 @@ class StockReadjustValuation(models.Model):
|
|||||||
self.state = 'draft'
|
self.state = 'draft'
|
||||||
|
|
||||||
def action_apply(self):
|
def action_apply(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.state not in ('calculated', 'processing'):
|
||||||
|
if self.state == 'done':
|
||||||
|
return
|
||||||
|
if self.state != 'calculated':
|
||||||
|
raise UserError(_("Please calculate first."))
|
||||||
|
|
||||||
|
# 0. Set to processing if not already
|
||||||
|
if self.state != 'processing':
|
||||||
|
self.state = 'processing'
|
||||||
|
self.env.cr.commit()
|
||||||
|
|
||||||
|
# Optimization: Pre-fetch data to avoid loops
|
||||||
|
product_ids = self.line_ids.mapped('product_id').ids
|
||||||
|
if not product_ids:
|
||||||
|
return
|
||||||
|
|
||||||
|
all_transactions = []
|
||||||
|
cost_map = {l.product_id.id: l.new_average_cost for l in self.line_ids}
|
||||||
|
|
||||||
|
# --- Batch Step 1: Initial Values ---
|
||||||
|
processed_initial_products = set()
|
||||||
|
existing_initial_ams = self.account_move_ids.filtered(lambda m: "Readjustment: Initial Value" in (m.ref or ""))
|
||||||
|
for am in existing_initial_ams:
|
||||||
|
for line in am.line_ids:
|
||||||
|
if line.product_id:
|
||||||
|
processed_initial_products.add(line.product_id.id)
|
||||||
|
|
||||||
|
for line in self.line_ids:
|
||||||
|
if line.product_id.id in processed_initial_products:
|
||||||
|
continue
|
||||||
|
|
||||||
|
initial_diff = line.target_initial_value - line.initial_value
|
||||||
|
if not float_is_zero(initial_diff, precision_digits=2):
|
||||||
|
self._create_correction_layer(line.product_id, initial_diff, self.date_start, _("Readjustment: Initial Value"))
|
||||||
|
|
||||||
|
line.product_id.sudo().write({'standard_price': line.new_average_cost})
|
||||||
|
|
||||||
|
# Commit after Step 1
|
||||||
|
self.env.cr.commit()
|
||||||
|
|
||||||
|
# --- Batch Step 2: Identify Moves ---
|
||||||
|
base_domain = [
|
||||||
|
('product_id', 'in', product_ids),
|
||||||
|
('create_date', '>=', self.date_start),
|
||||||
|
('create_date', '<=', self.date_end),
|
||||||
|
('company_id', '=', self.company_id.id)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Outgoing: Quantity < 0
|
||||||
|
outgoing_moves = self.env['stock.valuation.layer'].search(base_domain + [('quantity', '<', 0)]).mapped('stock_move_id')
|
||||||
|
|
||||||
|
# MFG Incoming: Quantity > 0 and Location Usage = Production
|
||||||
|
mfg_moves = self.env['stock.valuation.layer'].search(base_domain + [
|
||||||
|
('quantity', '>', 0),
|
||||||
|
('stock_move_id.location_id.usage', '=', 'production')
|
||||||
|
]).mapped('stock_move_id')
|
||||||
|
|
||||||
|
all_target_moves = outgoing_moves | mfg_moves
|
||||||
|
|
||||||
|
# --- IDEMPOTENCY CHECK ---
|
||||||
|
processed_move_ids = set()
|
||||||
|
if self.account_move_ids:
|
||||||
|
related_svls = self.env['stock.valuation.layer'].search([
|
||||||
|
('account_move_id', 'in', self.account_move_ids.ids)
|
||||||
|
])
|
||||||
|
processed_move_ids = set(related_svls.mapped('stock_move_id').ids)
|
||||||
|
|
||||||
|
moves_to_process = all_target_moves.filtered(lambda m: m.id not in processed_move_ids)
|
||||||
|
|
||||||
|
if not moves_to_process:
|
||||||
|
self.state = 'done'
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Batch Step 3: Fetch Data for Moves ---
|
||||||
|
all_layers_for_moves = self.env['stock.valuation.layer'].search([
|
||||||
|
('stock_move_id', 'in', moves_to_process.ids)
|
||||||
|
])
|
||||||
|
layers_by_move = defaultdict(lambda: self.env['stock.valuation.layer'])
|
||||||
|
for l in all_layers_for_moves:
|
||||||
|
layers_by_move[l.stock_move_id.id] |= l
|
||||||
|
|
||||||
|
move_to_am_map = {}
|
||||||
|
for move in moves_to_process:
|
||||||
|
am = move.account_move_ids.filtered(lambda m: m.state == 'posted')
|
||||||
|
if not am:
|
||||||
|
svls = layers_by_move[move.id]
|
||||||
|
am = svls.mapped('account_move_id').filtered(lambda m: m.state == 'posted')
|
||||||
|
if am:
|
||||||
|
move_to_am_map[move.id] = am[0]
|
||||||
|
|
||||||
|
# --- Batch Step 4: Process Outgoing (COGS) ---
|
||||||
|
outgoing_to_process = moves_to_process.filtered(lambda m: m in outgoing_moves)
|
||||||
|
for move in outgoing_to_process:
|
||||||
|
product_id = move.product_id.id
|
||||||
|
new_cost = cost_map.get(product_id)
|
||||||
|
if new_cost is None: continue
|
||||||
|
|
||||||
|
move_layers = layers_by_move[move.id]
|
||||||
|
qty_sold = abs(sum(move_layers.mapped('quantity')))
|
||||||
|
curr_val_abs = abs(sum(move_layers.mapped('value')))
|
||||||
|
|
||||||
|
target_value = qty_sold * new_cost
|
||||||
|
diff = target_value - curr_val_abs
|
||||||
|
|
||||||
|
if abs(diff) > 0.01:
|
||||||
|
original_am = move_to_am_map.get(move.id)
|
||||||
|
txs = self._prepare_correction_vals(move, -diff, original_am)
|
||||||
|
all_transactions.extend(txs)
|
||||||
|
|
||||||
|
# --- Batch Step 5: Process Manufacturing (Incoming) ---
|
||||||
|
mfg_to_process = moves_to_process.filtered(lambda m: m in mfg_moves)
|
||||||
|
for move in mfg_to_process:
|
||||||
|
production = move.production_id
|
||||||
|
if not production: continue
|
||||||
|
|
||||||
|
# Re-Calculate MO Cost
|
||||||
|
comp_cost = 0
|
||||||
|
for raw_move in production.move_raw_ids:
|
||||||
|
if raw_move.state != 'done': continue
|
||||||
|
p_id = raw_move.product_id.id
|
||||||
|
price = cost_map.get(p_id, raw_move.product_id.standard_price)
|
||||||
|
comp_cost += raw_move.product_uom_qty * price
|
||||||
|
|
||||||
|
all_out_layers = self.env['stock.valuation.layer'].search([('stock_move_id.production_id', '=', production.id)])
|
||||||
|
all_output_val = sum(all_out_layers.mapped('value'))
|
||||||
|
orig_raw_val = sum([abs(m.value) for m in production.move_raw_ids if m.state == 'done'])
|
||||||
|
|
||||||
|
overhead_total = all_output_val - orig_raw_val
|
||||||
|
new_total_cost = comp_cost + overhead_total
|
||||||
|
|
||||||
|
target_val = 0.0
|
||||||
|
if production.qty_produced > 0:
|
||||||
|
ratio = move.product_uom_qty / production.qty_produced
|
||||||
|
target_val = new_total_cost * ratio
|
||||||
|
|
||||||
|
current_val = sum(layers_by_move[move.id].mapped('value'))
|
||||||
|
diff = target_val - current_val
|
||||||
|
|
||||||
|
if abs(diff) > 0.01:
|
||||||
|
original_am = move_to_am_map.get(move.id)
|
||||||
|
txs = self._prepare_correction_vals(move, diff, original_am, is_mfg=True)
|
||||||
|
all_transactions.extend(txs)
|
||||||
|
|
||||||
|
# --- Batch Step 6: Chunked Create & Post ---
|
||||||
|
if not all_transactions:
|
||||||
|
self.state = 'done'
|
||||||
|
return
|
||||||
|
|
||||||
|
chunk_size = 50
|
||||||
|
created_moves = self.env['account.move']
|
||||||
|
|
||||||
|
# We need to process 'all_transactions' in chunks
|
||||||
|
for i in range(0, len(all_transactions), chunk_size):
|
||||||
|
chunk__txs = all_transactions[i:i+chunk_size]
|
||||||
|
|
||||||
|
# 1. Create Moves
|
||||||
|
move_vals_list = [t['move_vals'] for t in chunk__txs]
|
||||||
|
chunk_moves = self.env['account.move'].create(move_vals_list)
|
||||||
|
|
||||||
|
# 2. Post Moves
|
||||||
|
chunk_moves.action_post()
|
||||||
|
|
||||||
|
created_moves += chunk_moves
|
||||||
|
|
||||||
|
# 3. Create SVLs
|
||||||
|
svl_vals_list = []
|
||||||
|
for t, am in zip(chunk__txs, chunk_moves):
|
||||||
|
if t.get('svl_vals'):
|
||||||
|
vals = t['svl_vals']
|
||||||
|
vals['account_move_id'] = am.id
|
||||||
|
svl_vals_list.append(vals)
|
||||||
|
|
||||||
|
if svl_vals_list:
|
||||||
|
chunk_svls = self.env['stock.valuation.layer'].create(svl_vals_list)
|
||||||
|
|
||||||
|
# 4b. Force Dates SVL
|
||||||
|
tx_with_svl = [t for t in chunk__txs if t.get('svl_vals')]
|
||||||
|
date_map = defaultdict(list)
|
||||||
|
for svl, t in zip(chunk_svls, tx_with_svl):
|
||||||
|
date_map[t['date_svl']].append(svl.id)
|
||||||
|
|
||||||
|
for d, ids in date_map.items():
|
||||||
|
self._cr.execute("UPDATE stock_valuation_layer SET create_date = %s WHERE id IN %s", (d, tuple(ids)))
|
||||||
|
|
||||||
|
# 4a. Force Dates Moves (Per Chunk)
|
||||||
|
if chunk_moves:
|
||||||
|
self._cr.execute("UPDATE account_move SET date = %s WHERE id IN %s", (self.date_end.date(), tuple(chunk_moves.ids)))
|
||||||
|
|
||||||
|
# Link Created Moves to this record (Important for Resume Logic)
|
||||||
|
self.write({'account_move_ids': [(4, m.id) for m in chunk_moves]})
|
||||||
|
|
||||||
|
# COMMIT PER CHUNK
|
||||||
|
self.env.cr.commit()
|
||||||
|
_logger.info("Stock Readjustment: Processed chunk %s/%s", i + len(chunk__txs), len(all_transactions))
|
||||||
|
|
||||||
|
self.state = 'done'
|
||||||
|
|
||||||
|
def _prepare_correction_vals(self, move, amount, original_am=False, is_mfg=False):
|
||||||
|
"""
|
||||||
|
Returns list of transaction dicts:
|
||||||
|
[{
|
||||||
|
'move_vals': dict,
|
||||||
|
'svl_vals': dict (optional),
|
||||||
|
'date_move': date,
|
||||||
|
'date_svl': datetime
|
||||||
|
}]
|
||||||
|
"""
|
||||||
|
results = []
|
||||||
|
product = move.product_id
|
||||||
|
|
||||||
|
# 1. Accounts
|
||||||
|
debit_acc = False
|
||||||
|
credit_acc = False
|
||||||
|
|
||||||
|
if original_am:
|
||||||
|
accounts = product.product_tmpl_id.get_product_accounts(fiscal_pos=False)
|
||||||
|
valuation_acc_id = accounts['stock_valuation'].id
|
||||||
|
|
||||||
|
if is_mfg:
|
||||||
|
# For MFG: Dr Stock (Asset), Cr WIP (Other)
|
||||||
|
stock_line = original_am.line_ids.filtered(lambda l: l.account_id.id == valuation_acc_id)
|
||||||
|
wip_line = original_am.line_ids.filtered(lambda l: l.account_id.id != valuation_acc_id)
|
||||||
|
if stock_line and wip_line:
|
||||||
|
stock_id = stock_line[0].account_id.id
|
||||||
|
wip_id = wip_line[0].account_id.id
|
||||||
|
if amount > 0: # Increase Value
|
||||||
|
debit_acc, credit_acc = stock_id, wip_id
|
||||||
|
else:
|
||||||
|
debit_acc, credit_acc = wip_id, stock_id
|
||||||
|
else:
|
||||||
|
# For COGS: Dr Cost, Cr Stock
|
||||||
|
# If amount (SVL Val Adjust) < 0 (Cost increased): Dr Cost, Cr Stock
|
||||||
|
# If amount > 0 (Cost decreased): Dr Stock, Cr Cost
|
||||||
|
|
||||||
|
# amount passed here is "-diff" for COGS.
|
||||||
|
# diff = Target - Current.
|
||||||
|
# If Target (correct) > Current: diff > 0.
|
||||||
|
# amount = -diff (< 0).
|
||||||
|
# SVL Value = -amount = diff (> 0). Asset Increases.
|
||||||
|
# COGS Decreases?
|
||||||
|
# Wait.
|
||||||
|
# If we sold at 100 (Cost=100). Should be 110.
|
||||||
|
# SVL -100. Should be -110.
|
||||||
|
# We need key adjustment: -10.
|
||||||
|
# SVL Value Adjustment: -10.
|
||||||
|
|
||||||
|
# In action_apply logic:
|
||||||
|
# target = 110. current = 100.
|
||||||
|
# diff = 10.
|
||||||
|
# amount passed = -10.
|
||||||
|
# SVL Value = -(-10) = +10. (This increases inventory value??)
|
||||||
|
|
||||||
|
# Let's re-read action_apply logic carefully.
|
||||||
|
# target_value = 110. curr_val_abs = 100.
|
||||||
|
# diff = +10.
|
||||||
|
# amount passed = -10.
|
||||||
|
|
||||||
|
# SVL.value = -amount = -(-10) = +10.
|
||||||
|
# If we add +10 to inventory, we INCREASE stock value.
|
||||||
|
# But expected stock value for outgoing is NEGATIVE (credit).
|
||||||
|
# -100 + 10 = -90. This REDUCES the cost of goods sold.
|
||||||
|
# But we wanted cost to be 110 (Higher).
|
||||||
|
# So we need to add -10 to the layer.
|
||||||
|
|
||||||
|
# In _prepare:
|
||||||
|
# svl['value'] = -amount.
|
||||||
|
# We want svl['value'] to be -10.
|
||||||
|
# So -amount = -10 => amount = 10.
|
||||||
|
|
||||||
|
# Back to action_apply:
|
||||||
|
# I passed `self._prepare_correction_vals(move, -diff, ...)`
|
||||||
|
# If diff=10, amount=-10.
|
||||||
|
# svl['value'] = 10. WRONG.
|
||||||
|
|
||||||
|
# FIX in _prepare or action_apply:
|
||||||
|
# If I want to make stock value MORE NEGATIVE (Increase Cost),
|
||||||
|
# I need a negative adjustment.
|
||||||
|
# New Value (-110) = Old Value (-100) + Adj (-10).
|
||||||
|
# So passed 'amount' should be such that -amount = -10.
|
||||||
|
# So amount = 10.
|
||||||
|
# So I should pass `diff` (which is 10), not `-diff`.
|
||||||
|
|
||||||
|
# Wait, I passed `-diff` in my `action_apply` code.
|
||||||
|
# Line: `txs = self._prepare_correction_vals(move, -diff, original_am)`
|
||||||
|
# I should have passed `diff`.
|
||||||
|
|
||||||
|
# I CAN FIX THIS IN `_prepare_correction_vals` by flipping sign for COGS!
|
||||||
|
# Or strict adherence to args.
|
||||||
|
# Let's fix semantics here.
|
||||||
|
# 'amount' arg usually implies "Amount to Adjust".
|
||||||
|
# If I pass `diff`, then `svl['value']` will be `-diff` = `-10`. Correct.
|
||||||
|
|
||||||
|
# So `action_apply` passed `-diff`. So `svl['value']` becomes `+diff` (+10). Wrong.
|
||||||
|
# I need to invert logic in `_prepare` correction for COGS if I assume input is `-diff`?
|
||||||
|
# Or just treat `amount` as "Value Adjustment".
|
||||||
|
# Let's define: amount = The value to ADD to the SVL.
|
||||||
|
# `svl_vals['value'] = amount`.
|
||||||
|
# And `move_amount = abs(amount)`.
|
||||||
|
# Direction determined by sign.
|
||||||
|
|
||||||
|
# Existing OLD `_create_correction_from_move` had: `svl_vals['value'] = -amount`.
|
||||||
|
# And `action_apply` calculated `diff` and called it with `diff`.
|
||||||
|
# Logic: diff=10. call(10). svl=-10. Correct.
|
||||||
|
|
||||||
|
# In my NEW `action_apply`, I passed `-diff`.
|
||||||
|
# call(-10).
|
||||||
|
# If I keep `svl['value'] = -amount`:
|
||||||
|
# svl = -(-10) = 10. Wrong.
|
||||||
|
|
||||||
|
# Strategy: I will change `_prepare` to strictly use `amount` as the SVL Value Adjustment.
|
||||||
|
# `svl_vals['value'] = amount`.
|
||||||
|
# And I need to verify what I passed in `action_apply`.
|
||||||
|
# I passed `-diff` (which is -10).
|
||||||
|
# So `svl = -10`. Correct.
|
||||||
|
|
||||||
|
# So `svl_vals['value'] = amount` is the way.
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Let's Implement Correct Logic (ignoring legacy -amount swap overhead if possible)
|
||||||
|
# But I need to respect what I wrote in `action_apply` which is already saved.
|
||||||
|
# I passed `-diff` (e.g. -10).
|
||||||
|
# So I want SVL Value = -10.
|
||||||
|
# So I set `value: amount`.
|
||||||
|
|
||||||
|
# Accounts Direction:
|
||||||
|
# If amount < 0 (Stock Value Down / More Negative):
|
||||||
|
# COGS logic: Value -100 -> -110. (Cost Up).
|
||||||
|
# Dr COGS, Cr Stock.
|
||||||
|
# Debit = Expense. Credit = Stock.
|
||||||
|
# move_amount = 10.
|
||||||
|
|
||||||
|
# If amount > 0 (Stock Value Up / Less Negative):
|
||||||
|
# Value -100 -> -90. (Cost Down).
|
||||||
|
# Dr Stock, Cr COGS.
|
||||||
|
|
||||||
|
if not (debit_acc and credit_acc):
|
||||||
|
accounts = product.product_tmpl_id.get_product_accounts(fiscal_pos=False)
|
||||||
|
expense = accounts['expense'].id
|
||||||
|
stock = accounts['stock_output'].id or expense
|
||||||
|
val = accounts['stock_valuation'].id
|
||||||
|
|
||||||
|
if is_mfg:
|
||||||
|
debit_acc = val
|
||||||
|
credit_acc = stock
|
||||||
|
else:
|
||||||
|
debit_acc = stock
|
||||||
|
credit_acc = val
|
||||||
|
|
||||||
|
# Direction Swap based on Amount
|
||||||
|
# amount is the SVL Value Change.
|
||||||
|
# If amount < 0: Stock Value Decreases (Credit Stock). Dr Other.
|
||||||
|
# If amount > 0: Stock Value Increases (Debit Stock). Cr Other.
|
||||||
|
|
||||||
|
# Default assignment (Cost/WIP vs Stock):
|
||||||
|
# debit_acc = Cost/WIP
|
||||||
|
# credit_acc = Stock (Valuation)
|
||||||
|
|
||||||
|
move_amount = amount
|
||||||
|
if move_amount < 0:
|
||||||
|
# Stock Credit. Dr Cost, Cr Stock.
|
||||||
|
# Already defaults.
|
||||||
|
move_amount = abs(move_amount)
|
||||||
|
else:
|
||||||
|
# Stock Debit. Dr Stock, Cr Cost.
|
||||||
|
debit_acc, credit_acc = credit_acc, debit_acc
|
||||||
|
|
||||||
|
# POS Logic for LABEL and Reference
|
||||||
|
ref_text = f"{self.name} - Adj for {move.name}"
|
||||||
|
is_pos = False
|
||||||
|
pos_order_obj = False
|
||||||
|
pos_session_obj = False
|
||||||
|
|
||||||
|
# Reuse Logic
|
||||||
|
if move.origin:
|
||||||
|
pos_orders = self.env['pos.order'].search(['|', ('name', '=', move.origin), ('pos_reference', '=', move.origin)], limit=1)
|
||||||
|
if pos_orders:
|
||||||
|
is_pos = True
|
||||||
|
pos_order_obj = pos_orders[0]
|
||||||
|
pos_session_obj = pos_order_obj.session_id
|
||||||
|
if not is_pos:
|
||||||
|
sessions = self.env['pos.session'].search([('name', '=', move.origin)], limit=1)
|
||||||
|
if sessions:
|
||||||
|
is_pos = True
|
||||||
|
pos_session_obj = sessions[0]
|
||||||
|
|
||||||
|
if not is_pos and move.picking_id:
|
||||||
|
if 'pos_order_id' in move.picking_id._fields and move.picking_id.pos_order_id:
|
||||||
|
is_pos = True
|
||||||
|
pos_order_obj = move.picking_id.pos_order_id
|
||||||
|
pos_session_obj = pos_order_obj.session_id
|
||||||
|
if not is_pos and 'pos_session_id' in move.picking_id._fields and move.picking_id.pos_session_id:
|
||||||
|
is_pos = True
|
||||||
|
pos_session_obj = move.picking_id.pos_session_id
|
||||||
|
|
||||||
|
if not is_pos and move.picking_id and move.picking_id.origin:
|
||||||
|
sessions = self.env['pos.session'].search([('name', '=', move.picking_id.origin)], limit=1)
|
||||||
|
if sessions:
|
||||||
|
is_pos = True
|
||||||
|
pos_session_obj = sessions[0]
|
||||||
|
|
||||||
|
if not is_pos and move.picking_id:
|
||||||
|
pos_orders = self.env['pos.order'].search([('picking_ids', 'in', move.picking_id.id)], limit=1)
|
||||||
|
if pos_orders:
|
||||||
|
is_pos = True
|
||||||
|
# Weak link
|
||||||
|
pos_session_obj = pos_orders[0].session_id
|
||||||
|
|
||||||
|
if not is_pos:
|
||||||
|
candidates = [move.origin, move.reference, move.picking_id.name]
|
||||||
|
for c in candidates:
|
||||||
|
if c and ('POS' in c or 'Sesi' in c):
|
||||||
|
is_pos = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if is_pos:
|
||||||
|
details = []
|
||||||
|
if pos_order_obj:
|
||||||
|
details.append(pos_order_obj.name)
|
||||||
|
if pos_session_obj:
|
||||||
|
details.append(pos_session_obj.name)
|
||||||
|
|
||||||
|
if details:
|
||||||
|
ref_text = f"{self.name} - POS Adj for {move.name} ({' - '.join(details)})"
|
||||||
|
else:
|
||||||
|
ref_text = f"{self.name} - POS Adj for {move.name}"
|
||||||
|
|
||||||
|
# Product Name in Label
|
||||||
|
label_text = f"{_('Readjustment')} - {product.display_name}"
|
||||||
|
|
||||||
|
move_vals = {
|
||||||
|
'journal_id': self.journal_id.id,
|
||||||
|
'date': self.date_end.date(),
|
||||||
|
'ref': ref_text,
|
||||||
|
'move_type': 'entry',
|
||||||
|
'line_ids': [
|
||||||
|
(0, 0, {
|
||||||
|
'name': label_text,
|
||||||
|
'account_id': debit_acc,
|
||||||
|
'debit': move_amount,
|
||||||
|
'credit': 0,
|
||||||
|
'product_id': product.id,
|
||||||
|
}),
|
||||||
|
(0, 0, {
|
||||||
|
'name': label_text,
|
||||||
|
'account_id': credit_acc,
|
||||||
|
'debit': 0,
|
||||||
|
'credit': move_amount,
|
||||||
|
'product_id': product.id,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
svl_vals = {
|
||||||
|
'company_id': self.company_id.id,
|
||||||
|
'product_id': product.id,
|
||||||
|
'description': label_text,
|
||||||
|
'stock_move_id': move.id,
|
||||||
|
'quantity': 0,
|
||||||
|
'value': amount, # Direct usage of amount
|
||||||
|
'account_move_id': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
results.append({
|
||||||
|
'move_vals': move_vals,
|
||||||
|
'svl_vals': svl_vals,
|
||||||
|
'date_move': self.date_end.date(),
|
||||||
|
'date_svl': move.date
|
||||||
|
})
|
||||||
|
|
||||||
|
# Secondary POS Logic
|
||||||
|
if is_pos:
|
||||||
|
final_expense_acc = product.product_tmpl_id.get_product_accounts(fiscal_pos=False)['expense'].id
|
||||||
|
# debit_acc above was the Interim/Cost account if amount < 0.
|
||||||
|
# If amount < 0 (Cost Up), we debited Cost.
|
||||||
|
# We want to transfer that Cost to Final Expense.
|
||||||
|
# Dr Final, Cr Interim.
|
||||||
|
|
||||||
|
# If amount > 0 (Cost Down), we Credited Cost.
|
||||||
|
# We want to reduce Final Expense.
|
||||||
|
# Dr Interim, Cr Final.
|
||||||
|
|
||||||
|
# The 'debit_acc' calculated above might be Stock if amount > 0.
|
||||||
|
# We need the Interim Account specifically.
|
||||||
|
# Interim is the "Cost/Expense" account in the pair, regardless of direction.
|
||||||
|
# In my logic block:
|
||||||
|
# If amount < 0: debit_acc = StockOut/Expense(Interim), credit_acc = Stock.
|
||||||
|
# If amount > 0: debit_acc = Stock, credit_acc = Interim.
|
||||||
|
|
||||||
|
interim_acc = debit_acc if amount < 0 else credit_acc
|
||||||
|
|
||||||
|
if interim_acc != final_expense_acc:
|
||||||
|
# Create Secondary Move
|
||||||
|
|
||||||
|
# Direction:
|
||||||
|
# If amount < 0 (Cost Up): Transfer Cost Increase to Final. Dr Final, Cr Interim.
|
||||||
|
sec_debit = final_expense_acc
|
||||||
|
sec_credit = interim_acc
|
||||||
|
|
||||||
|
if amount > 0:
|
||||||
|
# Cost Down. Transfer Cost Decrease. Cr Final, Dr Interim.
|
||||||
|
sec_debit = interim_acc
|
||||||
|
sec_credit = final_expense_acc
|
||||||
|
|
||||||
|
move_vals_2 = {
|
||||||
|
'journal_id': self.journal_id.id,
|
||||||
|
'date': self.date_end.date(),
|
||||||
|
'ref': ref_text,
|
||||||
|
'move_type': 'entry',
|
||||||
|
'line_ids': [
|
||||||
|
(0, 0, {
|
||||||
|
'name': label_text,
|
||||||
|
'account_id': sec_debit,
|
||||||
|
'debit': move_amount,
|
||||||
|
'credit': 0,
|
||||||
|
'product_id': product.id,
|
||||||
|
}),
|
||||||
|
(0, 0, {
|
||||||
|
'name': label_text,
|
||||||
|
'account_id': sec_credit,
|
||||||
|
'debit': 0,
|
||||||
|
'credit': move_amount,
|
||||||
|
'product_id': product.id,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
results.append({
|
||||||
|
'move_vals': move_vals_2,
|
||||||
|
'svl_vals': False, # No SVL
|
||||||
|
'date_move': self.date_end.date(),
|
||||||
|
'date_svl': False
|
||||||
|
})
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def action_apply_IGNORED(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if self.state != 'calculated':
|
if self.state != 'calculated':
|
||||||
raise UserError(_("Please calculate first."))
|
raise UserError(_("Please calculate first."))
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
<button name="action_load_products" string="Load Products" type="object" class="oe_highlight" invisible="state != 'draft'"/>
|
<button name="action_load_products" string="Load Products" type="object" class="oe_highlight" invisible="state != 'draft'"/>
|
||||||
<button name="action_calculate" string="Calculate" type="object" class="oe_highlight" invisible="state != 'draft'"/>
|
<button name="action_calculate" string="Calculate" type="object" class="oe_highlight" invisible="state != 'draft'"/>
|
||||||
<button name="action_reset_to_draft" string="Reset to Draft" type="object" invisible="state != 'calculated'"/>
|
<button name="action_reset_to_draft" string="Reset to Draft" type="object" invisible="state != 'calculated'"/>
|
||||||
<button name="action_apply" string="Apply Readjustment" type="object" class="oe_highlight" invisible="state != 'calculated'"/>
|
<button name="action_apply" string="Apply Readjustment" type="object" class="oe_highlight" invisible="state not in ['calculated', 'processing']"/>
|
||||||
<field name="state" widget="statusbar" statusbar_visible="draft,calculated,done"/>
|
<field name="state" widget="statusbar" statusbar_visible="draft,calculated,processing,done"/>
|
||||||
</header>
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
<div class="oe_button_box" name="button_box">
|
<div class="oe_button_box" name="button_box">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user