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:
Parthiv Patel 2018-06-01 06:09:11 +00:00
commit b45da31481
20 changed files with 734 additions and 3 deletions

View File

@ -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

View File

@ -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',
} }

View File

@ -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.")

View File

@ -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):

View File

@ -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">

View 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

View 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,
}

View 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 ""

View File

@ -0,0 +1,3 @@
# Part of Flectra. See LICENSE file for full copyright and licensing details.
from . import stock_location

View 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()

View File

@ -0,0 +1,3 @@
# Part of Flectra. See LICENSE file for full copyright and licensing details.
from . import stock_ageing_report

View 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']

View File

@ -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>

View File

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,3 @@
# Part of Flectra. See LICENSE file for full copyright and licensing details.
from . import test_stock_ageing_report

View 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()

View File

@ -0,0 +1,3 @@
# Part of Flectra. See LICENSE file for full copyright and licensing details.
from . import stock_ageing_wizard

View 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)

View File

@ -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>