Merge branch 'master-stock-ageing-report' into 'master'
[ADD]Stock Ageing qweb Report: lists the age-wise break-up of Inventory to point… See merge request flectra-hq/flectra!62
This commit is contained in:
commit
b45da31481
@ -5,9 +5,8 @@ from . import controllers
|
|||||||
from . import models
|
from . import models
|
||||||
from . import report
|
from . import report
|
||||||
from . import wizard
|
from . import wizard
|
||||||
|
from flectra import api
|
||||||
|
from flectra.api import Environment, SUPERUSER_ID
|
||||||
from flectra import api, SUPERUSER_ID
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Apply proper fix & remove in master
|
# TODO: Apply proper fix & remove in master
|
||||||
@ -17,3 +16,13 @@ def pre_init_hook(cr):
|
|||||||
('model', 'like', '%stock%'),
|
('model', 'like', '%stock%'),
|
||||||
('module', '=', 'stock')
|
('module', '=', 'stock')
|
||||||
]).unlink()
|
]).unlink()
|
||||||
|
|
||||||
|
|
||||||
|
def post_init_check(cr, registery):
|
||||||
|
env = Environment(cr, SUPERUSER_ID, {})
|
||||||
|
move_obj = env['stock.move']
|
||||||
|
move_ids = move_obj.search([])
|
||||||
|
move_ids.set_move_type()
|
||||||
|
done_moves = move_obj.search([('state', '=', 'done')], order='date')
|
||||||
|
done_moves.check_move_bal_qty()
|
||||||
|
return True
|
||||||
|
@ -86,4 +86,5 @@
|
|||||||
'application': True,
|
'application': True,
|
||||||
'auto_install': False,
|
'auto_install': False,
|
||||||
'pre_init_hook': 'pre_init_hook',
|
'pre_init_hook': 'pre_init_hook',
|
||||||
|
'post_init_hook': 'post_init_check',
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,13 @@ class Location(models.Model):
|
|||||||
res['barcode'] = res['complete_name']
|
res['barcode'] = res['complete_name']
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def _should_be_valued(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.usage == 'internal' or (
|
||||||
|
self.usage == 'transit' and self.company_id):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
name = fields.Char('Location Name', required=True, translate=True)
|
name = fields.Char('Location Name', required=True, translate=True)
|
||||||
complete_name = fields.Char("Full Location Name", compute='_compute_complete_name', store=True)
|
complete_name = fields.Char("Full Location Name", compute='_compute_complete_name', store=True)
|
||||||
active = fields.Boolean('Active', default=True, help="By unchecking the active field, you may hide a location without deleting it.")
|
active = fields.Boolean('Active', default=True, help="By unchecking the active field, you may hide a location without deleting it.")
|
||||||
|
@ -159,6 +159,9 @@ class StockMove(models.Model):
|
|||||||
is_quantity_done_editable = fields.Boolean('Is quantity done editable', compute='_compute_is_quantity_done_editable')
|
is_quantity_done_editable = fields.Boolean('Is quantity done editable', compute='_compute_is_quantity_done_editable')
|
||||||
reference = fields.Char(compute='_compute_reference', string="Reference", store=True)
|
reference = fields.Char(compute='_compute_reference', string="Reference", store=True)
|
||||||
has_move_lines = fields.Boolean(compute='_compute_has_move_lines')
|
has_move_lines = fields.Boolean(compute='_compute_has_move_lines')
|
||||||
|
out_qty = fields.Float("Out Qty")
|
||||||
|
bal_qty = fields.Float("Balance")
|
||||||
|
move_type = fields.Selection([('in', 'In'), ('out', 'Out')], "Move type")
|
||||||
branch_id = fields.Many2one(
|
branch_id = fields.Many2one(
|
||||||
related='location_id.branch_id', store=True,
|
related='location_id.branch_id', store=True,
|
||||||
string='Source Location Branch',
|
string='Source Location Branch',
|
||||||
@ -394,6 +397,17 @@ class StockMove(models.Model):
|
|||||||
move.location_id.name, move.location_dest_id.name)))
|
move.location_id.name, move.location_dest_id.name)))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def set_move_type(self):
|
||||||
|
for self in self:
|
||||||
|
if self.picking_type_id and self.picking_type_id.code == 'incoming' or\
|
||||||
|
not self.location_id._should_be_valued() and \
|
||||||
|
self.location_dest_id._should_be_valued():
|
||||||
|
self.move_type = 'in'
|
||||||
|
elif self.picking_type_id and self.picking_type_id.code == 'outgoing' \
|
||||||
|
or self.location_id._should_be_valued() and not \
|
||||||
|
self.location_dest_id._should_be_valued():
|
||||||
|
self.move_type = 'out'
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
# TDE CLEANME: why doing this tracking on picking here ? seems weird
|
# TDE CLEANME: why doing this tracking on picking here ? seems weird
|
||||||
@ -403,6 +417,7 @@ class StockMove(models.Model):
|
|||||||
initial_values = {picking.id: {'state': picking.state}}
|
initial_values = {picking.id: {'state': picking.state}}
|
||||||
vals['ordered_qty'] = vals.get('product_uom_qty')
|
vals['ordered_qty'] = vals.get('product_uom_qty')
|
||||||
res = super(StockMove, self).create(vals)
|
res = super(StockMove, self).create(vals)
|
||||||
|
res.set_move_type()
|
||||||
if perform_tracking:
|
if perform_tracking:
|
||||||
picking.message_track(picking.fields_get(['state']), initial_values)
|
picking.message_track(picking.fields_get(['state']), initial_values)
|
||||||
return res
|
return res
|
||||||
@ -1045,6 +1060,28 @@ class StockMove(models.Model):
|
|||||||
break
|
break
|
||||||
return extra_move
|
return extra_move
|
||||||
|
|
||||||
|
def check_move_bal_qty(self):
|
||||||
|
for move in self:
|
||||||
|
if move.move_type == 'in':
|
||||||
|
move.bal_qty = move.product_uom_qty
|
||||||
|
if move.move_type == 'out':
|
||||||
|
prev_in_moves = self.search([
|
||||||
|
('product_id', '=', move.product_id.id),
|
||||||
|
('state', '=', 'done'),
|
||||||
|
('move_type', '=', 'in'),
|
||||||
|
('bal_qty', '>', '0'),
|
||||||
|
], order='date')
|
||||||
|
rem_qty = move.product_uom_qty
|
||||||
|
for pre_move in prev_in_moves:
|
||||||
|
if rem_qty:
|
||||||
|
pre_move.out_qty = move.product_uom_qty
|
||||||
|
if rem_qty > pre_move.bal_qty:
|
||||||
|
rem_qty -= pre_move.bal_qty
|
||||||
|
pre_move.bal_qty = 0
|
||||||
|
else:
|
||||||
|
pre_move.bal_qty -= rem_qty
|
||||||
|
rem_qty -= rem_qty
|
||||||
|
|
||||||
def _action_done(self):
|
def _action_done(self):
|
||||||
self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves
|
self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves
|
||||||
|
|
||||||
@ -1105,6 +1142,9 @@ class StockMove(models.Model):
|
|||||||
if picking:
|
if picking:
|
||||||
picking._create_backorder()
|
picking._create_backorder()
|
||||||
|
|
||||||
|
#calculate balance quantity & out qty to track the age of each stock
|
||||||
|
moves_todo.check_move_bal_qty()
|
||||||
|
|
||||||
return moves_todo
|
return moves_todo
|
||||||
|
|
||||||
def unlink(self):
|
def unlink(self):
|
||||||
|
@ -238,6 +238,9 @@
|
|||||||
<field name="name" invisible="1"/>
|
<field name="name" invisible="1"/>
|
||||||
<field name="date_expected" invisible="1"/>
|
<field name="date_expected" invisible="1"/>
|
||||||
<field name="date" attrs="{'invisible': [('state', '!=', 'done')]}"/>
|
<field name="date" attrs="{'invisible': [('state', '!=', 'done')]}"/>
|
||||||
|
<field name="out_qty" invisible="1"/>
|
||||||
|
<field name="bal_qty" invisible="1"/>
|
||||||
|
<field name="move_type" invisible="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group name="origin_grp" string="Origin">
|
<group name="origin_grp" string="Origin">
|
||||||
|
5
addons/stock_ageing_report/__init__.py
Normal file
5
addons/stock_ageing_report/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import wizard
|
||||||
|
from . import report
|
||||||
|
from . import models
|
31
addons/stock_ageing_report/__manifest__.py
Normal file
31
addons/stock_ageing_report/__manifest__.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Stock Ageing Report',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Stock',
|
||||||
|
'author': 'FlectraHQ',
|
||||||
|
'website': "https://flectrahq.com",
|
||||||
|
'depends': ['stock'],
|
||||||
|
'sequence': 40,
|
||||||
|
'summary': """Stock Ageing Report to display
|
||||||
|
product's stock by ageing period.""",
|
||||||
|
'description': """
|
||||||
|
Main Features
|
||||||
|
-------------
|
||||||
|
Stock Ageing Reports.
|
||||||
|
An Ageing Analysis Report in Flectra displays the age of the stock in hand.
|
||||||
|
This report lists the age-wise break-up of Inventory to point out old stock.
|
||||||
|
Flectra gives its users the flexibility to define their own ageing slabs.
|
||||||
|
""",
|
||||||
|
'data': [
|
||||||
|
'wizard/stock_ageing_wizard_view.xml',
|
||||||
|
'report/stock_ageing_report_view.xml',
|
||||||
|
'report/stock_ageing_report_template.xml',
|
||||||
|
],
|
||||||
|
'demo': [],
|
||||||
|
'qweb': [],
|
||||||
|
'auto_install': False,
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
}
|
189
addons/stock_ageing_report/i18n/stock_ageing_report.pot
Normal file
189
addons/stock_ageing_report/i18n/stock_ageing_report.pot
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# Translation of Flectra Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * stock_ageing_report
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Flectra Server 1.2\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2018-05-15 11:07+0000\n"
|
||||||
|
"PO-Revision-Date: 2018-05-15 11:07+0000\n"
|
||||||
|
"Last-Translator: <>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Brnach: </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Company: </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Date: </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Location: </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Period Length(days): </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Product Category: </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "<b>Warehouse: </b>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_branch_id
|
||||||
|
msgid "Branch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_wizard_form_view
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,help:stock_ageing_report.field_stock_ageing_wizard_date
|
||||||
|
msgid "Choose a date to get the inventory ageing report"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_company_id
|
||||||
|
msgid "Company"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_date
|
||||||
|
msgid "Date"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_report_stock_ageing_report_stock_ageing_report_template_display_name
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: code:addons/stock_ageing_report/report/stock_ageing_report.py:17
|
||||||
|
#, python-format
|
||||||
|
msgid "Form content is missing, this report cannot be printed."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_report_stock_ageing_report_stock_ageing_report_template_id
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model,name:stock_ageing_report.model_stock_location
|
||||||
|
msgid "Inventory Locations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_report_stock_ageing_report_stock_ageing_report_template___last_update
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard___last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_location_ids
|
||||||
|
msgid "Location"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_period_length
|
||||||
|
msgid "Period Length (days)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_wizard_form_view
|
||||||
|
msgid "Print"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_product_ids
|
||||||
|
msgid "Product"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_product_category_ids
|
||||||
|
msgid "Product Category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "Products"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_wizard_form_view
|
||||||
|
msgid "Select following details"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.actions.act_window,name:stock_ageing_report.action_stock_ageing_wizard
|
||||||
|
#: model:ir.actions.report,name:stock_ageing_report.action_stock_ageing_report
|
||||||
|
#: model:ir.ui.menu,name:stock_ageing_report.stock_ageing_wizard_menu
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "Stock Ageing Report"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.ui.view,arch_db:stock_ageing_report.stock_ageing_report_template
|
||||||
|
msgid "Total"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model.fields,field_description:stock_ageing_report.field_stock_ageing_wizard_warehouse_ids
|
||||||
|
msgid "Warehouse"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model,name:stock_ageing_report.model_stock_ageing_wizard
|
||||||
|
msgid "Wizard that opens the stock ageing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_ageing_report
|
||||||
|
#: model:ir.model,name:stock_ageing_report.model_report_stock_ageing_report_stock_ageing_report_template
|
||||||
|
msgid "report.stock_ageing_report.stock_ageing_report_template"
|
||||||
|
msgstr ""
|
||||||
|
|
3
addons/stock_ageing_report/models/__init__.py
Normal file
3
addons/stock_ageing_report/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import stock_location
|
26
addons/stock_ageing_report/models/stock_location.py
Normal file
26
addons/stock_ageing_report/models/stock_location.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from flectra import models, api
|
||||||
|
|
||||||
|
|
||||||
|
class Location(models.Model):
|
||||||
|
_inherit = "stock.location"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||||
|
args = args or []
|
||||||
|
domain = []
|
||||||
|
|
||||||
|
if self.env.context.get('warehouse', False):
|
||||||
|
warehouse_ids = self.env['stock.warehouse'].browse(
|
||||||
|
self.env.context['warehouse'][0][2])
|
||||||
|
lot_stock_ids = [wh.lot_stock_id.id for wh in warehouse_ids
|
||||||
|
if wh.lot_stock_id]
|
||||||
|
location_ids = self.env['stock.location'].search(
|
||||||
|
[('location_id', 'child_of', lot_stock_ids),
|
||||||
|
('usage', '=', 'internal')]).ids
|
||||||
|
location_ids += lot_stock_ids
|
||||||
|
domain = [('id', 'in', location_ids)]
|
||||||
|
|
||||||
|
recs = self.search(domain + args, limit=limit)
|
||||||
|
return recs.name_get()
|
3
addons/stock_ageing_report/report/__init__.py
Normal file
3
addons/stock_ageing_report/report/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import stock_ageing_report
|
161
addons/stock_ageing_report/report/stock_ageing_report.py
Normal file
161
addons/stock_ageing_report/report/stock_ageing_report.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from flectra import api, models, _
|
||||||
|
from flectra.exceptions import UserError
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from flectra.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
|
||||||
|
|
||||||
|
|
||||||
|
class StockAgeingReport(models.AbstractModel):
|
||||||
|
_name = 'report.stock_ageing_report.stock_ageing_report_template'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_report_values(self, docids, data=None):
|
||||||
|
if not data.get('form') or not self.env.context.get(
|
||||||
|
'active_model') or not self.env.context.get('active_id'):
|
||||||
|
raise UserError(
|
||||||
|
_("Form content is missing, this report cannot be printed."))
|
||||||
|
report = self.env['ir.actions.report']._get_report_from_name(
|
||||||
|
'stock_ageing_report.action_stock_ageing_report')
|
||||||
|
get_data = self._get_data(data)
|
||||||
|
return {
|
||||||
|
'doc_ids': docids,
|
||||||
|
'doc_model': report.model,
|
||||||
|
'docs': self,
|
||||||
|
'data': {
|
||||||
|
'company': [get_data['company_id']],
|
||||||
|
'branch': [get_data['branch_id']],
|
||||||
|
'warehouse': [get_data['warehouse_id']],
|
||||||
|
'location': [get_data['location_id']],
|
||||||
|
'product_category': [get_data['product_category_id']],
|
||||||
|
'product': [get_data['product_id']],
|
||||||
|
'period_length': [get_data['period_length']],
|
||||||
|
'date': [get_data['date']],
|
||||||
|
'period_list': get_data['period_list'],
|
||||||
|
'product_list': get_data['product_list']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_product_wise_detail(self, product_ids, location_id, date,
|
||||||
|
period_length, branch_id, company_id):
|
||||||
|
product_list = []
|
||||||
|
date_1 = datetime.strftime(date, DATETIME_FORMAT)
|
||||||
|
date_2 = datetime.strftime(
|
||||||
|
date - timedelta(days=period_length), DATETIME_FORMAT)
|
||||||
|
date_3 = datetime.strftime(
|
||||||
|
date - timedelta(days=(period_length * 2)), DATETIME_FORMAT)
|
||||||
|
date_4 = datetime.strftime(
|
||||||
|
date - timedelta(days=(period_length * 3)), DATETIME_FORMAT)
|
||||||
|
date_5 = datetime.strftime(
|
||||||
|
date - timedelta(days=(period_length * 4)), DATETIME_FORMAT)
|
||||||
|
date_period = [date_1, date_2, date_3, date_4, date_5]
|
||||||
|
final_dates = [
|
||||||
|
(date, date_period[date_period.index(date) + 1] if
|
||||||
|
date_period.index(date) < 4 else date)
|
||||||
|
for date in date_period]
|
||||||
|
for product in product_ids:
|
||||||
|
qty_period_list = self.calculate_virtual_qty(
|
||||||
|
product, location_id, branch_id, company_id, final_dates)
|
||||||
|
product_dict = {'product_id': product.name_get()[0][1],
|
||||||
|
'qty_period_list': qty_period_list,
|
||||||
|
'qty_available': sum(qty_period_list)}
|
||||||
|
product_list.append(product_dict)
|
||||||
|
return product_list
|
||||||
|
|
||||||
|
def calculate_virtual_qty(self, product, location_id, branch_id,
|
||||||
|
company_id, date_range):
|
||||||
|
product_list = []
|
||||||
|
quant_obj = self.env['stock.quant']
|
||||||
|
move_obj = self.env['stock.move']
|
||||||
|
for date in date_range:
|
||||||
|
domain = []
|
||||||
|
to_date = False
|
||||||
|
from_date = date[0]
|
||||||
|
if date[0] != date[1]:
|
||||||
|
to_date = date[1]
|
||||||
|
domain += [('product_id', '=', product.id)]
|
||||||
|
if product.tracking != 'none':
|
||||||
|
domain += [('location_id', 'in', location_id.ids),
|
||||||
|
('in_date', '<=', from_date),
|
||||||
|
('branch_id', '=', branch_id.id),
|
||||||
|
('company_id', '=', company_id.id)]
|
||||||
|
if to_date:
|
||||||
|
domain += [('in_date', '>', to_date)]
|
||||||
|
virtual_available = sum(
|
||||||
|
[quant.quantity for quant in quant_obj.search(domain)])
|
||||||
|
print ("\n domain", domain)
|
||||||
|
else:
|
||||||
|
domain += ['|', ('location_id', 'in', location_id.ids),
|
||||||
|
('location_dest_id', 'in', location_id.ids),
|
||||||
|
('state', '=', 'done'), ('bal_qty', '>', 0),
|
||||||
|
('date', '<=', from_date),
|
||||||
|
('branch_dest_id', '=', branch_id.id),
|
||||||
|
('company_id', '=', company_id.id)]
|
||||||
|
if to_date:
|
||||||
|
domain += [('date', '>', to_date)]
|
||||||
|
virtual_available = sum(
|
||||||
|
[move.bal_qty for move in move_obj.search(domain)])
|
||||||
|
product_list.append(virtual_available)
|
||||||
|
return product_list
|
||||||
|
|
||||||
|
def _get_data(self, data):
|
||||||
|
domain = []
|
||||||
|
company_id = self.env['res.company'].browse(data['form']['company_id'])
|
||||||
|
branch_id = self.env['res.branch'].browse(data['form']['branch_id'])
|
||||||
|
warehouse_id = self.env['stock.warehouse'].browse(
|
||||||
|
data['form']['warehouse_id'])
|
||||||
|
location_id = self.env['stock.location'].browse(
|
||||||
|
data['form']['location_id'])
|
||||||
|
product_category_id = self.env['product.category'].browse(
|
||||||
|
data['form']['product_category_id'])
|
||||||
|
product_id = self.env['product.product'].browse(
|
||||||
|
data['form']['product_id'])
|
||||||
|
if not location_id:
|
||||||
|
location_id = self.env['stock.location'].search([
|
||||||
|
('company_id', '=', company_id.id),
|
||||||
|
('usage', '=', 'internal')])
|
||||||
|
period_length = data['form']['period_length']
|
||||||
|
date = datetime.strptime(data['form']['date'], DATETIME_FORMAT)
|
||||||
|
|
||||||
|
if product_id:
|
||||||
|
domain += [('id', 'in', product_id.ids)]
|
||||||
|
elif product_category_id:
|
||||||
|
domain += [('categ_id', 'in', product_category_id.ids),
|
||||||
|
('type', '=', 'product')]
|
||||||
|
else:
|
||||||
|
domain += [('qty_available', '>', 0)]
|
||||||
|
product_ids = self.env['product.product'].search(domain)
|
||||||
|
product_list = self._get_product_wise_detail(
|
||||||
|
product_ids, location_id, date, period_length, branch_id,
|
||||||
|
company_id)
|
||||||
|
warehouse = location = product_category = ''
|
||||||
|
if warehouse_id:
|
||||||
|
warehouse = ','.join(
|
||||||
|
wh.name_get()[0][1] for wh in
|
||||||
|
warehouse_id) if len(warehouse_id) > 1 else\
|
||||||
|
warehouse_id.name_get()[0][1]
|
||||||
|
if data['form']['location_id']:
|
||||||
|
location = ','.join(loc.name_get()[0][1] for loc in location_id) \
|
||||||
|
if len(location_id) > 1 else location_id.name_get()[0][1] or ''
|
||||||
|
if product_category_id:
|
||||||
|
product_category = ','.join(
|
||||||
|
categ.name_get()[0][1] for categ in product_category_id
|
||||||
|
) if len(product_category_id) > 1 else\
|
||||||
|
product_category_id.name_get()[0][1] or ''
|
||||||
|
period_list = ['0 - ' + str(period_length),
|
||||||
|
str(period_length) + ' - ' + str(period_length * 2),
|
||||||
|
str(period_length * 2) + ' - ' + str(period_length * 3),
|
||||||
|
str(period_length * 3) + ' - ' + str(period_length * 4),
|
||||||
|
' + ' + str(period_length * 4)]
|
||||||
|
data['form'].update({
|
||||||
|
'company_id': company_id,
|
||||||
|
'branch_id': branch_id,
|
||||||
|
'warehouse_id': warehouse or '',
|
||||||
|
'location_id': location or '',
|
||||||
|
'product_category_id': product_category or '',
|
||||||
|
'product_id': product_id or '',
|
||||||
|
'period_length': period_length,
|
||||||
|
'date': datetime.strftime(date, DATETIME_FORMAT),
|
||||||
|
'period_list': period_list,
|
||||||
|
'product_list': product_list})
|
||||||
|
return data['form']
|
@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<flectra>
|
||||||
|
<template id="stock_ageing_report.stock_ageing_report_template">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<div class="page">
|
||||||
|
<h3>
|
||||||
|
<center>Stock Ageing Report</center>
|
||||||
|
</h3>
|
||||||
|
<br/>
|
||||||
|
<div>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>Company: </b></td>
|
||||||
|
<td t-foreach="data['company']" t-as="company_id">
|
||||||
|
<span t-esc="company_id.name"/>
|
||||||
|
</td>
|
||||||
|
<td><b>Period Length(days): </b></td>
|
||||||
|
<td t-foreach="data['period_length']" t-as="period_length">
|
||||||
|
<span t-esc="period_length"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>Brnach: </b></td>
|
||||||
|
<td t-foreach="data['branch']" t-as="branch_id">
|
||||||
|
<span t-esc="branch_id.name"/>
|
||||||
|
</td>
|
||||||
|
<td><b>Date: </b></td>
|
||||||
|
<td t-foreach="data['date']" t-as="date">
|
||||||
|
<span t-esc="date"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>Warehouse: </b></td>
|
||||||
|
<td t-foreach="data['warehouse']" t-as="warehouse_id">
|
||||||
|
<span t-esc="warehouse_id"/>
|
||||||
|
</td>
|
||||||
|
<td><b>Location: </b></td>
|
||||||
|
<td t-foreach="data['location']" t-as="location_id">
|
||||||
|
<span t-esc="location_id"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>Product Category: </b></td>
|
||||||
|
<td t-foreach="data['product_category']" t-as="product_category_id">
|
||||||
|
<span t-esc="product_category_id"/>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<table class="table table-condensed"
|
||||||
|
style="border-bottom: 0px solid white !important;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Products</th>
|
||||||
|
<th t-foreach="data['period_list']" t-as="period">
|
||||||
|
<span t-esc="period"/>
|
||||||
|
</th>
|
||||||
|
<th>Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr t-foreach="data['product_list']" t-as="product">
|
||||||
|
<td>
|
||||||
|
<span t-esc="product['product_id']"/>
|
||||||
|
</td>
|
||||||
|
<td t-foreach="product['qty_period_list']" t-as="qty">
|
||||||
|
<span t-esc="qty"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product['qty_available']"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</flectra>
|
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<flectra>
|
||||||
|
<report id="action_stock_ageing_report" model="stock.quant" report_type="qweb-pdf"
|
||||||
|
name="stock_ageing_report.stock_ageing_report_template" string="Stock Ageing Report"
|
||||||
|
file="stock_ageing_report.stock_ageing_report_template"/>
|
||||||
|
|
||||||
|
<record id="paperformat_ageing_report" model="report.paperformat">
|
||||||
|
<field name="name">Stock Ageing Report Format</field>
|
||||||
|
<field name="default" eval="True" />
|
||||||
|
<field name="format">A4</field>
|
||||||
|
<field name="page_height">0</field>
|
||||||
|
<field name="page_width">0</field>
|
||||||
|
<field name="orientation">Portrait</field>
|
||||||
|
<field name="margin_top">40</field>
|
||||||
|
<field name="margin_bottom">23</field>
|
||||||
|
<field name="margin_left">7</field>
|
||||||
|
<field name="margin_right">7</field>
|
||||||
|
<field name="header_line" eval="False" />
|
||||||
|
<field name="header_spacing">40</field>
|
||||||
|
<field name="dpi">90</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_stock_ageing_report" model="ir.actions.report">
|
||||||
|
<field name="print_report_name">'stock_ageing'+'-'+(object.name)</field>
|
||||||
|
<field name="paperformat_id" ref="paperformat_ageing_report"/>
|
||||||
|
</record>
|
||||||
|
</flectra>
|
BIN
addons/stock_ageing_report/static/description/icon.png
Normal file
BIN
addons/stock_ageing_report/static/description/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
3
addons/stock_ageing_report/tests/__init__.py
Normal file
3
addons/stock_ageing_report/tests/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import test_stock_ageing_report
|
29
addons/stock_ageing_report/tests/test_stock_ageing_report.py
Normal file
29
addons/stock_ageing_report/tests/test_stock_ageing_report.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from flectra.tests.common import TransactionCase
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
|
||||||
|
class TestStockAgeingReport(TransactionCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestStockAgeingReport, self).setUp()
|
||||||
|
self.location = self.env['stock.location']
|
||||||
|
self.location_barcode = self.env.ref('stock.stock_location_3')
|
||||||
|
self.barcode = self.location_barcode.barcode
|
||||||
|
self.warehouse = self.env["stock.warehouse"]
|
||||||
|
|
||||||
|
def test_20_stock_ageing_report(self):
|
||||||
|
# Print the Stock Ageing Report through the wizard
|
||||||
|
data_dict = {
|
||||||
|
'company_id': [],
|
||||||
|
'warehouse_ids': [],
|
||||||
|
'location_ids': [],
|
||||||
|
'product_category_ids': [(6, 0, [self.env.ref(
|
||||||
|
'product.product_category_5').id])],
|
||||||
|
'product_ids': [(6, 0, [self.env.ref(
|
||||||
|
'product.product_product_5b').id])],
|
||||||
|
'period_length': 30,
|
||||||
|
'date': date.today() - timedelta(days=30),
|
||||||
|
}
|
||||||
|
wizard = self.env['stock.ageing.wizard'].create(data_dict)
|
||||||
|
wizard.with_context(data_dict).print_report()
|
3
addons/stock_ageing_report/wizard/__init__.py
Normal file
3
addons/stock_ageing_report/wizard/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import stock_ageing_wizard
|
55
addons/stock_ageing_report/wizard/stock_ageing_wizard.py
Normal file
55
addons/stock_ageing_report/wizard/stock_ageing_wizard.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from flectra import api, models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class StockAgeingWizard(models.TransientModel):
|
||||||
|
|
||||||
|
_name = 'stock.ageing.wizard'
|
||||||
|
_description = 'Wizard that opens the stock ageing'
|
||||||
|
|
||||||
|
company_id = fields.Many2one('res.company', string="Company",
|
||||||
|
default=lambda self: self.env.user.company_id)
|
||||||
|
branch_id = fields.Many2one('res.branch', string="Branch",
|
||||||
|
default=lambda self:
|
||||||
|
self.env.user.default_branch_id)
|
||||||
|
warehouse_ids = fields.Many2many("stock.warehouse", string="Warehouse")
|
||||||
|
location_ids = fields.Many2many("stock.location", string='Location',
|
||||||
|
domain="[('usage', '=', 'internal')]")
|
||||||
|
product_category_ids = fields.Many2many("product.category",
|
||||||
|
string="Product Category")
|
||||||
|
product_ids = fields.Many2many('product.product', string='Product',
|
||||||
|
domain="[('type', '=', 'product')]")
|
||||||
|
period_length = fields.Integer(string='Period Length (days)', default=30)
|
||||||
|
date = fields.Datetime(string="Date",
|
||||||
|
help="Choose a date to get the inventory ageing "
|
||||||
|
"report",
|
||||||
|
default=fields.Datetime.now())
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def print_report(self):
|
||||||
|
"""
|
||||||
|
To get the Stock Ageing report and print the report
|
||||||
|
@return : return stock ageing report
|
||||||
|
"""
|
||||||
|
datas = {'ids': self._context.get('active_ids', [])}
|
||||||
|
res = self.read(
|
||||||
|
['company_id', 'branch_id', 'warehouse_ids', 'location_ids',
|
||||||
|
'product_category_ids', 'product_ids',
|
||||||
|
'period_length', 'date'])
|
||||||
|
for ageing_dict in res:
|
||||||
|
res = res and res[0] or {}
|
||||||
|
res['company_id'] = ageing_dict['company_id'] and\
|
||||||
|
ageing_dict['company_id'][0] or False
|
||||||
|
res['branch_id'] = ageing_dict['branch_id'] and \
|
||||||
|
ageing_dict['branch_id'][0] or False
|
||||||
|
res['warehouse_id'] = ageing_dict['warehouse_ids']
|
||||||
|
res['location_id'] = ageing_dict['location_ids']
|
||||||
|
res['product_category_id'] = ageing_dict['product_category_ids']
|
||||||
|
res['product_id'] = ageing_dict['product_ids']
|
||||||
|
res['period_length'] = ageing_dict['period_length'] or False
|
||||||
|
res['date'] = ageing_dict['date'] or False
|
||||||
|
datas['form'] = res
|
||||||
|
return self.env.ref(
|
||||||
|
'stock_ageing_report.action_stock_ageing_report'
|
||||||
|
'').report_action(self, data=datas)
|
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<flectra>
|
||||||
|
|
||||||
|
<record id="stock_ageing_wizard_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">Stock Ageing Wizard Form</field>
|
||||||
|
<field name="model">stock.ageing.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Select following details">
|
||||||
|
<group col="4">
|
||||||
|
<field name="company_id"/>
|
||||||
|
<field name="branch_id"/>
|
||||||
|
<field name="period_length" required="1"/>
|
||||||
|
<field name="date" required="1"/>
|
||||||
|
</group>
|
||||||
|
<seperator/>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="warehouse_ids" domain="[('company_id', '=', company_id)]" widget="many2many_tags"/>
|
||||||
|
<field name="product_category_ids" widget="many2many_tags"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="location_ids" context="{'warehouse': warehouse_ids}" widget="many2many_tags"/>
|
||||||
|
<field name="product_ids" domain="[('categ_id', 'in', product_category_ids)]" widget="many2many_tags"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button name="print_report" string="Print" type="object" class="btn-primary" />
|
||||||
|
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_stock_ageing_wizard" model="ir.actions.act_window">
|
||||||
|
<field name="name">Stock Ageing Report</field>
|
||||||
|
<field name="res_model">stock.ageing.wizard</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="context">{'company_id': context.get('company_id', False), 'warehouse_id':
|
||||||
|
context.get('warehouse_ids', False), 'location_id': context.get('location_ids', False),
|
||||||
|
'product_category_id': context.get('product_category_ids', False), 'product_id':
|
||||||
|
context.get('product_ids', False), 'period_length': context.get('period_length', False), 'date':
|
||||||
|
context.get('date', False)}</field>
|
||||||
|
<field name="view_id" ref="stock_ageing_wizard_form_view"/>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="stock_ageing_wizard_menu" name="Stock Ageing Report" parent="stock.menu_warehouse_report" sequence="2" action="action_stock_ageing_wizard"/>
|
||||||
|
|
||||||
|
</flectra>
|
Loading…
Reference in New Issue
Block a user