[MIG] stock_valuation_xlsx from v14 to v16
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
# Copyright 2020-2021 Akretion France (http://www.akretion.com)
|
# Copyright 2020-2024 Akretion France (http://www.akretion.com)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Stock Valuation XLSX',
|
'name': 'Stock Valuation XLSX',
|
||||||
'version': '14.0.1.0.0',
|
'version': '16.0.1.0.0',
|
||||||
'category': 'Tools',
|
'category': 'Tools',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'summary': 'Generate XLSX reports for past or present stock levels',
|
'summary': 'Generate XLSX reports for past or present stock levels',
|
||||||
@@ -42,8 +42,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
|||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'wizard/stock_valuation_xlsx_view.xml',
|
'wizard/stock_valuation_xlsx_view.xml',
|
||||||
'wizard/stock_variation_xlsx_view.xml',
|
'wizard/stock_variation_xlsx_view.xml',
|
||||||
'views/stock_inventory.xml',
|
|
||||||
'views/stock_expiry_depreciation_rule.xml',
|
'views/stock_expiry_depreciation_rule.xml',
|
||||||
],
|
],
|
||||||
'installable': False,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
# Copyright 2021-2024 Akretion France (http://www.akretion.com/)
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_stock_expiry_depreciation_rule_full,Full access on stock.expiry.depreciation.rule to account manager,model_stock_expiry_depreciation_rule,account.group_account_manager,1,1,1,1
|
access_stock_expiry_depreciation_rule_full,Full access on stock.expiry.depreciation.rule to account manager,model_stock_expiry_depreciation_rule,account.group_account_manager,1,1,1,1
|
||||||
access_stock_expiry_depreciation_rule_read,Read access on stock.expiry.depreciation.rule to stock manager,model_stock_expiry_depreciation_rule,stock.group_stock_manager,1,0,0,0
|
access_stock_expiry_depreciation_rule_read,Read access on stock.expiry.depreciation.rule to stock user,model_stock_expiry_depreciation_rule,stock.group_stock_user,1,0,0,0
|
||||||
access_stock_valuation_xlsx,stock.valuation.xlsx wizard,model_stock_valuation_xlsx,stock.group_stock_user,1,1,1,0
|
access_stock_valuation_xlsx,stock.valuation.xlsx wizard,model_stock_valuation_xlsx,stock.group_stock_user,1,1,1,0
|
||||||
access_stock_variation_xlsx,stock.variation.xlsx wizard,model_stock_variation_xlsx,stock.group_stock_user,1,1,1,0
|
access_stock_variation_xlsx,stock.variation.xlsx wizard,model_stock_variation_xlsx,stock.group_stock_user,1,1,1,0
|
||||||
|
|||||||
|
@@ -1,12 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2021 Akretion France (http://www.akretion.com/)
|
Copyright 2021-2024 Akretion France (http://www.akretion.com/)
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
|
||||||
|
|
||||||
<record id="stock_expiry_depreciation_rule_tree" model="ir.ui.view">
|
<record id="stock_expiry_depreciation_rule_tree" model="ir.ui.view">
|
||||||
<field name="model">stock.expiry.depreciation.rule</field>
|
<field name="model">stock.expiry.depreciation.rule</field>
|
||||||
@@ -31,5 +30,4 @@
|
|||||||
parent="account.account_management_menu"
|
parent="account.account_management_menu"
|
||||||
sequence="100"/>
|
sequence="100"/>
|
||||||
|
|
||||||
</data>
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2020 Akretion France (http://www.akretion.com/)
|
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
-->
|
|
||||||
|
|
||||||
<odoo>
|
|
||||||
|
|
||||||
|
|
||||||
<record id="view_inventory_form" model="ir.ui.view">
|
|
||||||
<field name="name">xlsx.stock.inventory.form</field>
|
|
||||||
<field name="model">stock.inventory</field>
|
|
||||||
<field name="inherit_id" ref="stock.view_inventory_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<button name="action_validate" position="after">
|
|
||||||
<button name="%(stock_valuation_xlsx_action)d" type="action"
|
|
||||||
states="done" string="XLSX Valuation Report"
|
|
||||||
context="{'default_source': 'inventory', 'default_inventory_id': active_id}"/>
|
|
||||||
</button>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2020 Akretion France (http://www.akretion.com/)
|
# Copyright 2020-2024 Akretion France (http://www.akretion.com/)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -29,33 +29,26 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
'stock.warehouse', string='Warehouse', check_company=True,
|
'stock.warehouse', string='Warehouse', check_company=True,
|
||||||
domain="[('company_id', '=', company_id)]")
|
domain="[('company_id', '=', company_id)]")
|
||||||
location_id = fields.Many2one(
|
location_id = fields.Many2one(
|
||||||
'stock.location', string='Root Stock Location', required=True,
|
'stock.location', string='Root Stock Location', required=True, check_company=True,
|
||||||
domain="[('usage', 'in', ('view', 'internal')), ('company_id', '=', company_id)]",
|
compute='_compute_location_id', readonly=False, precompute=True, store=True,
|
||||||
default=lambda self: self._default_location(), check_company=True,
|
domain="[('usage', 'in', ('view', 'internal')), ('company_id', 'in', [False, company_id])]",
|
||||||
help="The childen locations of the selected locations will "
|
help="The childen locations of the selected locations will "
|
||||||
"be taken in the valuation.")
|
"be taken in the valuation.")
|
||||||
categ_ids = fields.Many2many(
|
categ_ids = fields.Many2many(
|
||||||
'product.category', string='Product Category Filter',
|
'product.category', string='Product Category Filter',
|
||||||
help="Leave this field empty to have a stock valuation for all your products.",
|
help="Leave this field empty to have a stock valuation for all your products.",
|
||||||
)
|
)
|
||||||
source = fields.Selection([
|
|
||||||
('inventory', 'Physical Inventory'),
|
|
||||||
('stock', 'Stock Levels'),
|
|
||||||
], string='Source data', default='stock', required=True)
|
|
||||||
inventory_id = fields.Many2one(
|
|
||||||
'stock.inventory', string='Inventory', check_company=True,
|
|
||||||
domain="[('state', '=', 'done'), ('company_id', '=', company_id)]")
|
|
||||||
stock_date_type = fields.Selection([
|
stock_date_type = fields.Selection([
|
||||||
('present', 'Present'),
|
('present', 'Present'),
|
||||||
('past', 'Past'),
|
('past', 'Past'),
|
||||||
], string='Present or Past', default='present')
|
], string='Present or Past', default='present', required=True)
|
||||||
past_date = fields.Datetime(
|
past_date = fields.Datetime(
|
||||||
string='Past Date', default=fields.Datetime.now)
|
string='Past Date', default=fields.Datetime.now)
|
||||||
categ_subtotal = fields.Boolean(
|
categ_subtotal = fields.Boolean(
|
||||||
string='Subtotals per Categories', default=True,
|
string='Subtotals per Categories', default=True,
|
||||||
help="Show a subtotal per product category.")
|
help="Show a subtotal per product category.")
|
||||||
standard_price_date = fields.Selection([
|
standard_price_date = fields.Selection([
|
||||||
('past', 'Past Date or Inventory Date'),
|
('past', 'Past Date'),
|
||||||
('present', 'Current'),
|
('present', 'Current'),
|
||||||
], default='past', string='Cost Price Date')
|
], default='past', string='Cost Price Date')
|
||||||
has_expiry_date = fields.Boolean(
|
has_expiry_date = fields.Boolean(
|
||||||
@@ -67,38 +60,28 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _default_has_expiry_date(self):
|
def _default_has_expiry_date(self):
|
||||||
splo = self.env['stock.production.lot']
|
|
||||||
has_expiry_date = False
|
has_expiry_date = False
|
||||||
if hasattr(splo, 'expiry_date'):
|
if hasattr(self.env['stock.lot'], 'expiry_date'):
|
||||||
has_expiry_date = True
|
has_expiry_date = True
|
||||||
return has_expiry_date
|
return has_expiry_date
|
||||||
|
|
||||||
@api.model
|
@api.depends('warehouse_id', 'company_id')
|
||||||
def _default_location(self):
|
def _compute_location_id(self):
|
||||||
wh = self.env.ref('stock.warehouse0')
|
for wiz in self:
|
||||||
return wh.lot_stock_id
|
wh = wiz.warehouse_id
|
||||||
|
if not wh:
|
||||||
|
wh = self.env["stock.warehouse"].search([('company_id', '=', wiz.company_id.id)], limit=1)
|
||||||
|
if wh:
|
||||||
|
wiz.location_id = wh.view_location_id.id
|
||||||
|
|
||||||
@api.onchange('warehouse_id')
|
def _check_config(self):
|
||||||
def warehouse_id_change(self):
|
|
||||||
if self.warehouse_id:
|
|
||||||
self.location_id = self.warehouse_id.view_location_id.id
|
|
||||||
|
|
||||||
def _check_config(self, company_id):
|
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if (
|
if (
|
||||||
self.source == 'stock' and
|
|
||||||
self.stock_date_type == 'past' and
|
self.stock_date_type == 'past' and
|
||||||
self.past_date > fields.Datetime.now()):
|
self.past_date > fields.Datetime.now()):
|
||||||
raise UserError(_("The 'Past Date' must be in the past !"))
|
raise UserError(_("The 'Past Date' must be in the past !"))
|
||||||
if self.source == 'inventory':
|
|
||||||
if not self.inventory_id:
|
|
||||||
raise UserError(_("You must select an inventory."))
|
|
||||||
elif self.inventory_id.state != 'done':
|
|
||||||
raise UserError(_(
|
|
||||||
"The selected inventory (%s) is not in done state.")
|
|
||||||
% self.inventory_id.display_name)
|
|
||||||
cost_method_real_count = self.env['ir.property'].sudo().search([
|
cost_method_real_count = self.env['ir.property'].sudo().search([
|
||||||
('company_id', '=', company_id),
|
('company_id', '=', self.company_id.id),
|
||||||
('name', '=', 'property_cost_method'),
|
('name', '=', 'property_cost_method'),
|
||||||
('value_text', '=', 'real'),
|
('value_text', '=', 'real'),
|
||||||
('type', '=', 'selection'),
|
('type', '=', 'selection'),
|
||||||
@@ -159,7 +142,7 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
if not std_price_date: # present
|
if not std_price_date: # present
|
||||||
product_id2data[p['id']][std_price_field_name] = p['standard_price']
|
product_id2data[p['id']][std_price_field_name] = p['standard_price']
|
||||||
else:
|
else:
|
||||||
layer_rg = svlo.read_group(
|
layer_rg = svlo._read_group(
|
||||||
[
|
[
|
||||||
('product_id', '=', p['id']),
|
('product_id', '=', p['id']),
|
||||||
('company_id', '=', company_id),
|
('company_id', '=', company_id),
|
||||||
@@ -201,13 +184,13 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def prodlot_id2data(self, product_ids, has_expiry_date, depreciation_rules):
|
def prodlot_id2data(self, product_ids, has_expiry_date, depreciation_rules):
|
||||||
splo = self.env['stock.production.lot']
|
slo = self.env['stock.lot']
|
||||||
lot_id2data = {}
|
lot_id2data = {}
|
||||||
lot_fields = ['name']
|
lot_fields = ['name']
|
||||||
if has_expiry_date:
|
if has_expiry_date:
|
||||||
lot_fields.append('expiry_date')
|
lot_fields.append('expiry_date')
|
||||||
|
|
||||||
lots = splo.search_read(
|
lots = slo.search_read(
|
||||||
[('product_id', 'in', product_ids)], lot_fields)
|
[('product_id', 'in', product_ids)], lot_fields)
|
||||||
for lot in lots:
|
for lot in lots:
|
||||||
lot_id2data[lot['id']] = lot
|
lot_id2data[lot['id']] = lot
|
||||||
@@ -230,30 +213,6 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
loc_id2name[loc['id']] = loc['display_name']
|
loc_id2name[loc['id']] = loc['display_name']
|
||||||
return loc_id2name
|
return loc_id2name
|
||||||
|
|
||||||
def compute_data_from_inventory(self, product_ids, prec_qty):
|
|
||||||
self.ensure_one()
|
|
||||||
logger.debug('Start compute_data_from_inventory')
|
|
||||||
# Can he modify UoM ?
|
|
||||||
inv_lines = self.env['stock.inventory.line'].search_read([
|
|
||||||
('inventory_id', '=', self.inventory_id.id),
|
|
||||||
('location_id', 'child_of', self.location_id.id),
|
|
||||||
('product_id', 'in', product_ids),
|
|
||||||
('product_qty', '>', 0),
|
|
||||||
], ['product_id', 'location_id', 'prod_lot_id', 'product_qty'])
|
|
||||||
res = []
|
|
||||||
in_stock_products = {}
|
|
||||||
for l in inv_lines:
|
|
||||||
if not float_is_zero(l['product_qty'], precision_digits=prec_qty):
|
|
||||||
res.append({
|
|
||||||
'product_id': l['product_id'][0],
|
|
||||||
'lot_id': l['prod_lot_id'] and l['prod_lot_id'][0] or False,
|
|
||||||
'qty': l['product_qty'],
|
|
||||||
'location_id': l['location_id'][0],
|
|
||||||
})
|
|
||||||
in_stock_products[l['product_id'][0]] = True
|
|
||||||
logger.debug('End compute_data_from_inventory')
|
|
||||||
return res, in_stock_products
|
|
||||||
|
|
||||||
def compute_data_from_present_stock(self, company_id, product_ids, prec_qty):
|
def compute_data_from_present_stock(self, company_id, product_ids, prec_qty):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
logger.debug('Start compute_data_from_present_stock')
|
logger.debug('Start compute_data_from_present_stock')
|
||||||
@@ -361,40 +320,34 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
company = self.company_id
|
company = self.company_id
|
||||||
company_id = company.id
|
company_id = company.id
|
||||||
prec_cur_rounding = company.currency_id.rounding
|
prec_cur_rounding = company.currency_id.rounding
|
||||||
self._check_config(company_id)
|
self._check_config()
|
||||||
|
|
||||||
apply_depreciation = self.apply_depreciation
|
|
||||||
if (
|
if (
|
||||||
(self.source == 'stock' and self.stock_date_type == 'past') or
|
self.stock_date_type == 'past' or
|
||||||
not self.split_by_lot or
|
not self.split_by_lot or
|
||||||
not self.has_expiry_date):
|
not self.has_expiry_date):
|
||||||
apply_depreciation = False
|
apply_depreciation = False
|
||||||
|
else:
|
||||||
|
apply_depreciation = self.apply_depreciation
|
||||||
product_ids = self.get_product_ids()
|
product_ids = self.get_product_ids()
|
||||||
if not product_ids:
|
if not product_ids:
|
||||||
raise UserError(_("There are no products to analyse."))
|
raise UserError(_("There are no products to analyse."))
|
||||||
split_by_lot = self.split_by_lot
|
if self.stock_date_type == 'present':
|
||||||
split_by_location = self.split_by_location
|
split_by_lot = self.split_by_lot
|
||||||
if self.source == 'stock':
|
split_by_location = self.split_by_location
|
||||||
if self.stock_date_type == 'present':
|
past_date = False
|
||||||
past_date = False
|
|
||||||
data, in_stock_products = self.compute_data_from_present_stock(
|
|
||||||
company_id, product_ids, prec_qty)
|
|
||||||
elif self.stock_date_type == 'past':
|
|
||||||
split_by_lot = False
|
|
||||||
split_by_location = False
|
|
||||||
past_date = self.past_date
|
|
||||||
data, in_stock_products = self.compute_data_from_past_stock(
|
|
||||||
product_ids, prec_qty, past_date)
|
|
||||||
elif self.source == 'inventory':
|
|
||||||
past_date = self.inventory_id.date
|
|
||||||
data, in_stock_products = self.compute_data_from_inventory(product_ids, prec_qty)
|
|
||||||
if self.source == 'stock' and self.stock_date_type == 'present':
|
|
||||||
standard_price_past_date = False
|
standard_price_past_date = False
|
||||||
else: # field standard_price_date is shown on screen
|
data, in_stock_products = self.compute_data_from_present_stock(
|
||||||
if self.standard_price_date == 'present':
|
company_id, product_ids, prec_qty)
|
||||||
standard_price_past_date = False
|
elif self.stock_date_type == 'past':
|
||||||
else:
|
split_by_lot = False
|
||||||
standard_price_past_date = past_date
|
split_by_location = False
|
||||||
|
past_date = self.past_date
|
||||||
|
standard_price_past_date = past_date
|
||||||
|
data, in_stock_products = self.compute_data_from_past_stock(
|
||||||
|
product_ids, prec_qty, past_date)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
depreciation_rules = []
|
depreciation_rules = []
|
||||||
if apply_depreciation:
|
if apply_depreciation:
|
||||||
depreciation_rules = self._prepare_expiry_depreciation_rules(company_id, past_date)
|
depreciation_rules = self._prepare_expiry_depreciation_rules(company_id, past_date)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 Akretion France (http://www.akretion.com/)
|
Copyright 2020-2024 Akretion France (http://www.akretion.com/)
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
@@ -12,25 +12,24 @@
|
|||||||
<field name="name">stock.valuation.xlsx.form</field>
|
<field name="name">stock.valuation.xlsx.form</field>
|
||||||
<field name="model">stock.valuation.xlsx</field>
|
<field name="model">stock.valuation.xlsx</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Stock valuation XLSX">
|
<form>
|
||||||
<div name="help">
|
<div name="help">
|
||||||
<p>The generated XLSX report has the valuation of stockable products located on the selected stock locations (and their childrens).</p>
|
<p>The generated XLSX report has the valuation of stockable products located on the selected stock locations (and their childrens).</p>
|
||||||
</div>
|
</div>
|
||||||
<group name="setup">
|
<group name="setup">
|
||||||
<field name="company_id" groups="base.group_multi_company"/>
|
<field name="company_id" groups="base.group_multi_company"/>
|
||||||
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="categ_ids" widget="many2many_tags"/>
|
<field name="categ_ids" widget="many2many_tags"/>
|
||||||
<field name="warehouse_id"/>
|
<field name="warehouse_id"/>
|
||||||
<field name="location_id"/>
|
<field name="location_id"/>
|
||||||
<field name="source" widget="radio"/>
|
<field name="stock_date_type" />
|
||||||
<field name="inventory_id" attrs="{'invisible': [('source', '!=', 'inventory')], 'required': [('source', '=', 'inventory')]}"/>
|
<field name="past_date" attrs="{'invisible': [('stock_date_type', '!=', 'past')], 'required': [('stock_date_type', '=', 'past')]}"/>
|
||||||
<field name="stock_date_type" attrs="{'invisible': [('source', '!=', 'stock')], 'required': [('source', '=', 'stock')]}" widget="radio"/>
|
<field name="standard_price_date" attrs="{'invisible': [('stock_date_type', '=', 'present')]}" widget="radio"/>
|
||||||
<field name="past_date" attrs="{'invisible': ['|', ('source', '!=', 'stock'), ('stock_date_type', '!=', 'past')], 'required': [('source', '=', 'stock'), ('stock_date_type', '=', 'past')]}"/>
|
|
||||||
<field name="standard_price_date" attrs="{'invisible': [('source', '=', 'stock'), ('stock_date_type', '=', 'present')]}" widget="radio"/>
|
|
||||||
<field name="categ_subtotal" />
|
<field name="categ_subtotal" />
|
||||||
<field name="has_expiry_date" invisible="1"/>
|
<field name="has_expiry_date" invisible="1"/>
|
||||||
<field name="split_by_lot" attrs="{'invisible': [('source', '=', 'stock'), ('stock_date_type', '=', 'past')]}" groups="stock.group_production_lot"/>
|
<field name="split_by_lot" attrs="{'invisible': [('stock_date_type', '=', 'past')]}" groups="stock.group_production_lot"/>
|
||||||
<field name="split_by_location" attrs="{'invisible': [('source', '=', 'stock'), ('stock_date_type', '=', 'past')]}"/>
|
<field name="split_by_location" attrs="{'invisible': [('stock_date_type', '=', 'past')]}"/>
|
||||||
<field name="apply_depreciation" groups="stock.group_production_lot" attrs="{'invisible': ['|', '|', ('split_by_lot', '=', False), ('has_expiry_date', '=', False), '&', ('source', '=', 'stock'), ('stock_date_type', '=', 'past')]}"/>
|
<field name="apply_depreciation" groups="stock.group_production_lot" attrs="{'invisible': ['|', '|', ('split_by_lot', '=', False), ('has_expiry_date', '=', False), ('stock_date_type', '=', 'past')]}"/>
|
||||||
</group>
|
</group>
|
||||||
<footer>
|
<footer>
|
||||||
<button name="generate" type="object" class="btn-primary" string="Generate"/>
|
<button name="generate" type="object" class="btn-primary" string="Generate"/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2020-2021 Akretion France (http://www.akretion.com/)
|
# Copyright 2020-2024 Akretion France (http://www.akretion.com/)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -27,9 +27,9 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
'stock.warehouse', string='Warehouse', check_company=True,
|
'stock.warehouse', string='Warehouse', check_company=True,
|
||||||
domain="[('company_id', '=', company_id)]")
|
domain="[('company_id', '=', company_id)]")
|
||||||
location_id = fields.Many2one(
|
location_id = fields.Many2one(
|
||||||
'stock.location', string='Root Stock Location', required=True,
|
'stock.location', string='Root Stock Location', required=True, check_company=True,
|
||||||
domain="[('usage', 'in', ('view', 'internal')), ('company_id', '=', company_id)]",
|
compute='_compute_location_id', readonly=False, precompute=True, store=True,
|
||||||
default=lambda self: self._default_location(), check_company=True,
|
domain="[('usage', 'in', ('view', 'internal')), ('company_id', 'in', [False, company_id])]",
|
||||||
help="The childen locations of the selected locations will "
|
help="The childen locations of the selected locations will "
|
||||||
"be taken in the valuation.")
|
"be taken in the valuation.")
|
||||||
categ_ids = fields.Many2many(
|
categ_ids = fields.Many2many(
|
||||||
@@ -56,17 +56,16 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
string='Subtotals per Categories', default=True,
|
string='Subtotals per Categories', default=True,
|
||||||
help="Show a subtotal per product category.")
|
help="Show a subtotal per product category.")
|
||||||
|
|
||||||
@api.model
|
@api.depends('warehouse_id', 'company_id')
|
||||||
def _default_location(self):
|
def _compute_location_id(self):
|
||||||
wh = self.env.ref('stock.warehouse0')
|
for wiz in self:
|
||||||
return wh.lot_stock_id
|
wh = wiz.warehouse_id
|
||||||
|
if not wh:
|
||||||
|
wh = self.env["stock.warehouse"].search([('company_id', '=', wiz.company_id.id)], limit=1)
|
||||||
|
if wh:
|
||||||
|
wiz.location_id = wh.view_location_id.id
|
||||||
|
|
||||||
@api.onchange('warehouse_id')
|
def _check_config(self):
|
||||||
def warehouse_id_change(self):
|
|
||||||
if self.warehouse_id:
|
|
||||||
self.location_id = self.warehouse_id.view_location_id.id
|
|
||||||
|
|
||||||
def _check_config(self, company_id):
|
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
present = fields.Datetime.now()
|
present = fields.Datetime.now()
|
||||||
if self.end_date_type == 'past':
|
if self.end_date_type == 'past':
|
||||||
@@ -80,7 +79,7 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
if self.start_date >= present:
|
if self.start_date >= present:
|
||||||
raise UserError(_("The start date must be in the past."))
|
raise UserError(_("The start date must be in the past."))
|
||||||
cost_method_real_count = self.env['ir.property'].sudo().search([
|
cost_method_real_count = self.env['ir.property'].sudo().search([
|
||||||
('company_id', '=', company_id),
|
('company_id', '=', self.company_id.id),
|
||||||
('name', '=', 'property_cost_method'),
|
('name', '=', 'property_cost_method'),
|
||||||
('value_text', '=', 'real'),
|
('value_text', '=', 'real'),
|
||||||
('type', '=', 'selection'),
|
('type', '=', 'selection'),
|
||||||
@@ -119,21 +118,21 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
domain_quant = [('product_id', 'in', product_ids)] + domain_quant_loc
|
domain_quant = [('product_id', 'in', product_ids)] + domain_quant_loc
|
||||||
domain_move_in = [('product_id', 'in', product_ids), ('state', '=', 'done')] + domain_move_in_loc
|
domain_move_in = [('product_id', 'in', product_ids), ('state', '=', 'done')] + domain_move_in_loc
|
||||||
domain_move_out = [('product_id', 'in', product_ids), ('state', '=', 'done')] + domain_move_out_loc
|
domain_move_out = [('product_id', 'in', product_ids), ('state', '=', 'done')] + domain_move_out_loc
|
||||||
quants_res = dict((item['product_id'][0], item['quantity']) for item in sqo.read_group(domain_quant, ['product_id', 'quantity'], ['product_id'], orderby='id'))
|
quants_res = dict((item['product_id'][0], item['quantity']) for item in sqo._read_group(domain_quant, ['product_id', 'quantity'], ['product_id'], orderby='id'))
|
||||||
domain_move_in_start_to_end = [('date', '>', start_date)] + domain_move_in
|
domain_move_in_start_to_end = [('date', '>', start_date)] + domain_move_in
|
||||||
domain_move_out_start_to_end = [('date', '>', start_date)] + domain_move_out
|
domain_move_out_start_to_end = [('date', '>', start_date)] + domain_move_out
|
||||||
if end_date_type == 'past':
|
if end_date_type == 'past':
|
||||||
|
|
||||||
domain_move_in_end_to_present = [('date', '>', end_date)] + domain_move_in
|
domain_move_in_end_to_present = [('date', '>', end_date)] + domain_move_in
|
||||||
domain_move_out_end_to_present = [('date', '>', end_date)] + domain_move_out
|
domain_move_out_end_to_present = [('date', '>', end_date)] + domain_move_out
|
||||||
moves_in_res_end_to_present = dict((item['product_id'][0], item['product_qty']) for item in smo.read_group(domain_move_in_end_to_present, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
moves_in_res_end_to_present = dict((item['product_id'][0], item['product_qty']) for item in smo._read_group(domain_move_in_end_to_present, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
moves_out_res_end_to_present = dict((item['product_id'][0], item['product_qty']) for item in smo.read_group(domain_move_out_end_to_present, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
moves_out_res_end_to_present = dict((item['product_id'][0], item['product_qty']) for item in smo._read_group(domain_move_out_end_to_present, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
|
|
||||||
domain_move_in_start_to_end += [('date', '<', end_date)]
|
domain_move_in_start_to_end += [('date', '<', end_date)]
|
||||||
domain_move_out_start_to_end += [('date', '<', end_date)]
|
domain_move_out_start_to_end += [('date', '<', end_date)]
|
||||||
|
|
||||||
moves_in_res_start_to_end = dict((item['product_id'][0], item['product_qty']) for item in smo.read_group(domain_move_in_start_to_end, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
moves_in_res_start_to_end = dict((item['product_id'][0], item['product_qty']) for item in smo._read_group(domain_move_in_start_to_end, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
moves_out_res_start_to_end = dict((item['product_id'][0], item['product_qty']) for item in smo.read_group(domain_move_out_start_to_end, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
moves_out_res_start_to_end = dict((item['product_id'][0], item['product_qty']) for item in smo._read_group(domain_move_out_start_to_end, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
|
|
||||||
product_data = {} # key = product_id , value = dict
|
product_data = {} # key = product_id , value = dict
|
||||||
for product in ppo.browse(product_ids):
|
for product in ppo.browse(product_ids):
|
||||||
@@ -208,7 +207,7 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
company = self.company_id
|
company = self.company_id
|
||||||
company_id = company.id
|
company_id = company.id
|
||||||
prec_cur_rounding = company.currency_id.rounding
|
prec_cur_rounding = company.currency_id.rounding
|
||||||
self._check_config(company_id)
|
self._check_config()
|
||||||
|
|
||||||
product_ids = self.get_product_ids()
|
product_ids = self.get_product_ids()
|
||||||
if not product_ids:
|
if not product_ids:
|
||||||
|
|||||||
@@ -12,12 +12,13 @@
|
|||||||
<field name="name">stock.variation.xlsx.form</field>
|
<field name="name">stock.variation.xlsx.form</field>
|
||||||
<field name="model">stock.variation.xlsx</field>
|
<field name="model">stock.variation.xlsx</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Stock variation XLSX">
|
<form>
|
||||||
<div name="help">
|
<div name="help">
|
||||||
<p>The generated XLSX report has the valuation of stockable products located on the selected stock locations (and their childrens).</p>
|
<p>The generated XLSX report has the valuation of stockable products located on the selected stock locations (and their childrens).</p>
|
||||||
</div>
|
</div>
|
||||||
<group name="setup">
|
<group name="setup">
|
||||||
<field name="company_id" groups="base.group_multi_company"/>
|
<field name="company_id" groups="base.group_multi_company"/>
|
||||||
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="categ_ids" widget="many2many_tags"/>
|
<field name="categ_ids" widget="many2many_tags"/>
|
||||||
<field name="warehouse_id"/>
|
<field name="warehouse_id"/>
|
||||||
<field name="location_id"/>
|
<field name="location_id"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user