trytonpsk-stock_co/stock.py

1034 lines
42 KiB
Python
Raw Normal View History

2021-08-26 20:16:55 +02:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2022-01-18 22:23:18 +01:00
import copy
2021-12-29 17:41:02 +01:00
from datetime import date, datetime, timedelta
2021-08-28 18:27:55 +02:00
from decimal import Decimal
2021-08-26 20:16:55 +02:00
from trytond.model import fields, ModelView
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, Not, Bool
from trytond.wizard import Wizard, StateView, StateReport, Button
from trytond.report import Report
from trytond.transaction import Transaction
2023-10-24 21:58:28 +02:00
2021-08-26 20:16:55 +02:00
STATES = {'invisible': (Eval('type') != 'goods')}
STATES_MOVE = {
'readonly': Eval('state').in_(['cancel', 'assigned', 'done']),
}
class Lot(metaclass=PoolMeta):
__name__ = 'stock.lot'
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
2021-08-26 20:16:55 +02:00
class Move(metaclass=PoolMeta):
__name__ = 'stock.move'
2022-08-30 19:18:05 +02:00
description = fields.Char('Description')
2021-08-26 20:16:55 +02:00
current_stock = fields.Function(fields.Float('Current Stock',
depends=['product']), 'on_change_with_current_stock')
2021-12-09 20:10:33 +01:00
reference = fields.Function(fields.Char('Reference',
depends=['product'], help='reference of product'), 'get_reference')
2021-08-26 20:16:55 +02:00
2021-09-25 07:02:37 +02:00
@fields.depends('current_stock')
2021-08-26 20:16:55 +02:00
def on_change_product(self, name=None):
super(Move, self).on_change_product()
self.current_stock = self.on_change_with_current_stock()
2021-12-09 20:10:33 +01:00
@fields.depends('product')
def get_reference(self, name=None):
reference = None
if self.product and hasattr(self.product, 'reference') and self.product.reference:
reference = self.product.reference
return reference
2021-08-26 20:16:55 +02:00
@fields.depends('product', 'from_location', 'to_location')
def on_change_with_current_stock(self, name=None):
res = 0
location = None
2022-01-24 15:57:35 +01:00
if self.from_location and self.from_location.type == 'storage' and self.from_location.parent:
2021-08-26 20:16:55 +02:00
location = self.from_location.parent.storage_location
2022-01-24 15:57:35 +01:00
elif self.to_location and self.to_location.type == 'storage' and self.to_location.parent:
2021-08-26 20:16:55 +02:00
location = self.to_location.parent.storage_location
if self.product and location:
context = {
'location_ids': [location.id],
'stock_date_end': date.today(),
}
with Transaction().set_context(context):
res_dict = self.product._get_quantity(
2021-09-25 07:02:37 +02:00
[self.product],
'quantity',
[location.id],
grouping_filter=([self.product.id],)
)
2021-08-26 20:16:55 +02:00
if res_dict.get(self.product.id):
res += res_dict[self.product.id]
2022-02-18 16:22:54 +01:00
return res
2021-08-26 20:16:55 +02:00
2022-02-21 18:27:26 +01:00
@fields.depends('product')
def on_change_with_description(self, name=None):
res = ''
2022-10-11 17:50:04 +02:00
if self.product and self.product.description:
2022-02-21 18:27:26 +01:00
description = self.product.description
res = description.rstrip('\n')
return res
2022-04-27 00:20:35 +02:00
@classmethod
def do(cls, moves):
super(Move, cls).do(moves)
for move in moves:
2023-09-14 22:50:19 +02:00
product = move.product
2022-10-10 23:46:59 +02:00
if not move.description:
move.description = cls.on_change_with_description(move)
move.save()
2022-06-08 00:35:08 +02:00
# remove origin 'stock.inventory.line' for set average cost
if move.origin and move.origin.__name__ in ('purchase.line'):
2023-09-14 22:50:19 +02:00
if product.cost_price_method == 'fixed':
product.cost_price = move.unit_price
product.save()
2022-04-27 00:20:35 +02:00
move.set_average_cost()
2023-09-27 20:06:59 +02:00
if hasattr(product, 'standard_margin') and product.standard_margin and product.standard_margin > 0 and move.product.cost_price > 0:
2023-09-14 22:50:19 +02:00
template = product.template
2023-09-14 23:31:21 +02:00
percentage_calculation = round(move.product.cost_price * Decimal(move.product.standard_margin/100), 4)
2023-09-14 22:50:19 +02:00
template.list_price = product.cost_price + percentage_calculation
template.on_change_list_price()
template.save()
2022-04-27 00:20:35 +02:00
def set_average_cost(self):
AverageCost = Pool().get('product.average_cost')
data = {
"stock_move": self.id,
"product": self.product.id,
"effective_date": self.effective_date,
"cost_price": self.product.cost_price,
}
AverageCost.create([data])
2021-08-26 20:16:55 +02:00
class MoveByProductStart(ModelView):
'Move By Product Start'
__name__ = 'stock_co.move_by_product.start'
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
from_location = fields.Many2One('stock.location', 'From Location')
to_location = fields.Many2One('stock.location', 'To Location',
required=True)
categories = fields.Many2Many('product.category', None, None, 'Categories')
company = fields.Many2One('company.company', 'Company',
required=True)
party = fields.Many2One('party.party', 'Party')
shipment_draft = fields.Boolean('Shipment Draft')
start_time = fields.Time('Start Time',)
end_time = fields.Time('End Time', states={
'required': Bool(Eval('start_time')),
'invisible': Not(Bool(Eval('start_time'))),
}, depends=['start_time'])
product = fields.Many2One('product.product', 'Product')
brand = fields.Many2One('product.brand', 'Brand')
2021-08-26 20:16:55 +02:00
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_end_date():
Date_ = Pool().get('ir.date')
return Date_.today()
@staticmethod
def default_start_date():
Date_ = Pool().get('ir.date')
return Date_.today()
class PrintMoveByProduct(Wizard):
'Move By Product'
__name__ = 'stock_co.print_move_by_product'
start = StateView('stock_co.move_by_product.start',
'stock_co.print_move_by_product_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-print', default=True),
])
print_ = StateReport('stock_co.move_by_product')
def do_print_(self, action):
category_ids = []
from_location_id = None
brand_id = None
party_id = None
start_time = None
end_time = None
if self.start.from_location:
from_location_id = self.start.from_location.id
if self.start.categories:
category_ids = [acc.id for acc in self.start.categories]
if self.start.brand:
2021-08-26 20:16:55 +02:00
brand_id = self.start.brand.id
if self.start.party:
party_id = self.start.party.id
if self.start.start_time:
start_time = self.start.start_time
if self.start.end_time:
end_time = self.start.end_time
data = {
'company': self.start.company.id,
'from_location': from_location_id,
'to_location': self.start.to_location.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'categories': category_ids,
'brand': brand_id,
'party': party_id,
'shipment_draft': self.start.shipment_draft,
'start_time': start_time,
'end_time': end_time,
'product': self.start.product.id if self.start.product else None
}
return action, data
def transition_print_(self):
return 'end'
class MoveByProduct(Report):
'Move By Product Report'
__name__ = 'stock_co.move_by_product'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
Move = pool.get('stock.move')
Location = pool.get('stock.location')
Party = pool.get('party.party')
Category = pool.get('product.template-product.category')
company = Company(data['company'])
from_location = None
brand_name = ''
party_name = ''
start_date = None
end_date = None
categories = Category.search([('category', 'in', data['categories'])])
products_ids = [c.template.id for c in categories]
dom_products = []
if data['start_time']:
start_date = datetime.combine(data['start_date'], data['start_time'])
end_date = datetime.combine(data['end_date'], data['end_time'])
_start_date = Company.convert_timezone(start_date, True)
_end_date = Company.convert_timezone(end_date, True)
dom_products.append(
('to_location.id', '=', data['to_location']),
('create_date', '>=', _start_date),
('create_date', '<=', _end_date)
)
else:
start_date = data['start_date']
end_date = data['end_date']
dom_products.extend([
('to_location', '=', data['to_location']),
['AND',
['OR', [
('planned_date', '>=', start_date),
('planned_date', '<=', end_date),
], [
('effective_date', '>=', start_date),
('effective_date', '<=', end_date),
],
]]
])
if data['shipment_draft']:
dom_products.append(('state', '=', 'draft'))
if data['from_location']:
dom_products.append(
('from_location.id', '=', data['from_location']),
)
from_location = Location(data['from_location'])
if data['categories']:
if products_ids:
dom_products.append(
('product.template', 'in', products_ids)
)
else:
dom_products.append(
('product.template.account_category', 'in', data['categories'])
)
if data['brand']:
Brand = pool.get('product.brand')
2021-08-26 20:16:55 +02:00
dom_products.append(
('product.template.brand', '=', data['brand']),
)
brand_name = Brand(data['brand']).name
if data['party']:
dom_products.append(
('invoice_lines.invoice.party', '=', data['party']),
)
party_name = Party(data['party']).name
if data['product']:
dom_products.append(
('product', '=', data['product']),
)
moves = Move.search([dom_products])
products = {}
Location = pool.get('stock.location')
location = Location(data['to_location'])
2021-08-26 20:16:55 +02:00
for move in moves:
product = move.product
amount = move.quantity * float(product.cost_price)
origin = None
2023-09-23 19:39:30 +02:00
id_dict = product.id
if location.type == 'customer':
2023-09-23 19:39:30 +02:00
id_dict = move.origin.sale.party.name + str(product.id)
origin = move.origin.sale.party.name if move.origin else None
2021-08-26 20:16:55 +02:00
try:
2023-09-23 19:39:30 +02:00
row = products[id_dict]
row[4].append(move.quantity)
row[6].append(amount)
2021-08-26 20:16:55 +02:00
except Exception as e:
template = product.template
brand = template.brand and template.brand.name
category = template.account_category and template.account_category.name
effective_date = move.effective_date
2023-09-23 19:39:30 +02:00
products[id_dict] = [
2021-08-26 20:16:55 +02:00
product.code,
origin,
2021-08-26 20:16:55 +02:00
product.rec_name,
template.default_uom.symbol,
[move.quantity],
product.cost_price,
[amount],
category,
brand,
template.list_price,
effective_date,
2021-08-26 20:16:55 +02:00
]
2023-09-23 19:39:30 +02:00
2021-08-26 20:16:55 +02:00
report_context['records'] = products.values()
report_context['from_location'] = from_location
report_context['to_location'] = location
report_context['customer_location'] = location.type == 'customer'
2021-08-26 20:16:55 +02:00
report_context['start_date'] = start_date
report_context['end_date'] = end_date
report_context['company'] = company
report_context['brand_name'] = brand_name
report_context['party_name'] = party_name
return report_context
class WarehouseStockStart(ModelView):
'Warehouse Stock Start'
__name__ = 'stock_co.warehouse_stock.start'
company = fields.Many2One('company.company', 'Company', required=True)
locations = fields.Many2Many('stock.location', None, None, "Locations",
domain=[
('type', 'in', ['storage', 'customer']),
('active', '=', True)
], required=True)
to_date = fields.Date('To Date', required=True)
only_minimal_level = fields.Boolean('Only Minimal Level')
group_by_location = fields.Boolean('Group By Location')
2022-01-12 15:35:56 +01:00
group_by_supplier = fields.Boolean('Group By Supplier')
2021-08-26 20:16:55 +02:00
category = fields.Many2One('product.category', 'Category')
2022-01-12 15:35:56 +01:00
suppliers = fields.Many2Many('party.party', None, None, "Suppliers",
domain=[
('active', '=', True)
])
2021-09-24 05:16:20 +02:00
zero_quantity = fields.Boolean('Zero Quantity')
2021-08-26 20:16:55 +02:00
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_to_date():
Date_ = Pool().get('ir.date')
return Date_.today()
class WarehouseStock(Wizard):
'Warehouse Stock'
__name__ = 'stock_co.warehouse_stock'
start = StateView('stock_co.warehouse_stock.start',
'stock_co.warehouse_stock_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('stock_co.warehouse_stock.report')
def do_print_(self, action):
category_id = None
if self.start.category:
category_id = self.start.category.id
data = {
'company': self.start.company.id,
'locations': [l.id for l in self.start.locations],
'location_names': ', '.join([l.name for l in self.start.locations]),
'only_minimal_level': self.start.only_minimal_level,
'group_by_location': self.start.group_by_location,
2022-01-12 15:35:56 +01:00
'group_by_supplier': self.start.group_by_supplier,
2021-09-24 05:16:20 +02:00
'zero_quantity': self.start.zero_quantity,
2021-08-26 20:16:55 +02:00
'to_date': self.start.to_date,
'category': category_id,
2022-01-12 15:35:56 +01:00
'suppliers': [l.id for l in self.start.suppliers],
2021-08-26 20:16:55 +02:00
}
return action, data
class WarehouseReport(Report):
'Warehouse Report'
__name__ = 'stock_co.warehouse_stock.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
Product = pool.get('product.product')
OrderPoint = pool.get('stock.order_point')
Location = pool.get('stock.location')
ids_location = data['locations']
locations = Location.browse(data['locations'])
2023-03-27 15:44:21 +02:00
Move = pool.get('stock.move')
2021-08-26 20:16:55 +02:00
dom_products = [
('active', '=', True),
('template.active', '=', True),
('type', '=', 'goods'),
('consumable', '=', False),
]
stock_context = {
'stock_date_end': data['to_date'],
'locations': ids_location,
}
if data['category']:
dom_products.append(['AND', ['OR', [
('account_category', '=', data['category']),
], [
('categories', 'in', [data['category']]),
],
]])
2021-09-24 05:16:20 +02:00
if not data['zero_quantity']:
2021-08-26 20:16:55 +02:00
dom_products.append([('quantity', '!=', 0)])
if data['only_minimal_level']:
locations = Location.search([('id', 'in', ids_location)])
location_parents = [location.parent.id for location in locations]
order_points_wl = OrderPoint.search([
('warehouse_location', 'in', set(location_parents)),
2021-08-26 20:16:55 +02:00
('type', '=', 'purchase'),
])
min_quantities = {op.product.id: op.min_quantity for op in order_points_wl}
order_points_sl = OrderPoint.search([
('storage_location', 'in', ids_location),
('type', '=', 'internal'),
])
min_quantities.update({op.product.id: op.min_quantity for op in order_points_sl})
2021-08-26 20:16:55 +02:00
products_ids = min_quantities.keys()
dom_products.append(('id', 'in', products_ids))
2022-01-12 15:35:56 +01:00
if data['suppliers']:
2022-01-14 00:00:08 +01:00
dom_products.append([('template.product_suppliers.party', 'in', data['suppliers'])])
2021-08-26 20:16:55 +02:00
total_amount = 0
total_amount_imp = 0
2021-08-26 20:16:55 +02:00
values = {}
products = []
if data['group_by_location']:
for l in locations:
2023-09-12 00:16:57 +02:00
days = []
2021-08-26 20:16:55 +02:00
stock_context['locations'] = [l.id]
with Transaction().set_context(stock_context):
prdts = Product.search(dom_products, order=[('code', 'ASC')])
2022-01-12 15:35:56 +01:00
suppliers = {}
2023-03-27 15:44:21 +02:00
for p in prdts:
move = Move.search([
('to_location', '=', l.id),
('product', '=', p.id),
2023-04-03 15:31:01 +02:00
('state', '=', 'done'),
], order=[('effective_date', 'DESC')])
2023-09-12 00:16:57 +02:00
date_start = move[0].effective_date if len(move) > 0 else None
2023-03-27 15:44:21 +02:00
date_now = date.today()
2023-05-29 16:43:10 +02:00
date_result = date_now - date_start if date_start else 0
days.append(date_result.days) if date_start else days.append('error')
2022-01-12 15:35:56 +01:00
if data['group_by_supplier']:
for p in prdts:
if not p.template.product_suppliers:
continue
2022-01-14 00:00:08 +01:00
for prod_sup in p.template.product_suppliers:
sup_id = prod_sup.party.id
2022-01-13 18:38:19 +01:00
try:
suppliers[sup_id]['products'].append(p)
2022-05-14 17:09:14 +02:00
suppliers[sup_id]['total_amount'].append(p.amount_cost if p.amount_cost else 0)
suppliers[sup_id]['total_amount_imp'].append(p.extra_tax * Deciaml(p.quantity) if p.extra_tax else 0)
2022-01-13 18:38:19 +01:00
except:
suppliers[sup_id] = {}
suppliers[sup_id]['products'] = [p]
2022-01-14 00:00:08 +01:00
suppliers[sup_id]['party'] = prod_sup.party
2022-05-14 17:09:14 +02:00
suppliers[sup_id]['total_amount'] = [p.amount_cost if p.amount_cost else 0]
suppliers[sup_id]['total_amount_imp'] = [p.extra_tax * Decimal(p.quantity) if p.extra_tax else 0]
2022-04-27 00:20:35 +02:00
total_amount = sum([p.amount_cost for p in prdts if p.amount_cost])
total_amount_imp = sum([p.extra_tax * Decimal(p.quantity) for p in prdts if p.extra_tax])
2021-08-26 20:16:55 +02:00
values[l.id] = {
'name': l.name,
'products': prdts,
2023-03-27 15:44:21 +02:00
'days': days,
2022-01-12 15:35:56 +01:00
'suppliers': suppliers.values(),
'total_amount': total_amount,
'total_amount_imp': total_amount_imp
2021-08-26 20:16:55 +02:00
}
products = values.values()
else:
with Transaction().set_context(stock_context):
products = Product.search(dom_products, order=[('code', 'ASC')])
2022-01-12 15:35:56 +01:00
if data['only_minimal_level']:
products = [p for p in products if p.quantity <= min_quantities[p.id]]
2022-04-27 00:20:35 +02:00
total_amount = sum([p.amount_cost for p in products if p.amount_cost])
total_amount_imp = sum([p.extra_tax * Decimal(p.quantity) for p in products if p.extra_tax])
2022-01-12 15:35:56 +01:00
suppliers = {}
if data['group_by_supplier']:
for p in products:
if not p.template.product_suppliers:
continue
2022-01-14 00:00:08 +01:00
for prod_sup in p.template.product_suppliers:
sup_id = prod_sup.party.id
2022-01-13 18:38:19 +01:00
try:
suppliers[sup_id]['products'].append(p)
2022-05-14 17:05:15 +02:00
suppliers[sup_id]['total_amount'].append(p.amount_cost if p.amount_cost else 0)
suppliers[sup_id]['total_amount_imp'].append(p.extra_tax * Decimal(p.quantity) if p.extra_tax else 0)
2022-01-13 18:38:19 +01:00
except:
suppliers[sup_id] = {}
suppliers[sup_id]['products'] = [p]
2022-01-14 00:00:08 +01:00
suppliers[sup_id]['party'] = prod_sup.party
2022-05-14 17:05:15 +02:00
suppliers[sup_id]['total_amount'] = [p.amount_cost if p.amount_cost else 0]
suppliers[sup_id]['total_amount_imp'] = [p.extra_tax * Decimal(p.quantity) if p.extra_tax else 0]
2022-01-12 15:35:56 +01:00
products = suppliers.values()
2021-08-26 20:16:55 +02:00
2021-09-13 20:14:58 +02:00
cursor = Transaction().connection.cursor()
2023-03-27 15:44:21 +02:00
# query = "select distinct on(p.id) p.id, t.name, p.code, s.effective_date from product_product as p right join stock_move as s on p.id=s.product join product_template as t on p.template=t.id where (date_part('day', TIMESTAMP '{}') - date_part('day', s.effective_date))>45 and s.shipment ilike 'stock.shipment.in,%' and state='done' order by p.id, s.effective_date DESC;".format(data['to_date'])
2021-09-13 20:14:58 +02:00
query = "select distinct on(p.id) p.id, t.name, p.code, s.effective_date from product_product as p right join stock_move as s on p.id=s.product join product_template as t on p.template=t.id where s.shipment ilike 'stock.shipment.in,%' and state='done' order by p.id, s.effective_date DESC;"
2023-03-27 15:44:21 +02:00
2021-09-13 20:14:58 +02:00
cursor.execute(query)
columns = list(cursor.description)
result = cursor.fetchall()
2021-09-24 05:16:20 +02:00
last_purchase = {}
2021-09-13 20:14:58 +02:00
for row in result:
row_dict = {}
for i, col in enumerate(columns):
row_dict[col.name] = row[i]
2021-09-24 05:16:20 +02:00
last_purchase[row[0]] = row_dict
2021-09-13 20:14:58 +02:00
2021-08-26 20:16:55 +02:00
report_context['group_by_location'] = data['group_by_location']
2022-01-12 15:35:56 +01:00
report_context['group_by_supplier'] = data['group_by_supplier']
2021-08-26 20:16:55 +02:00
report_context['records'] = products
report_context['total_amount'] = total_amount
report_context['total_amount_imp'] = total_amount_imp
2021-09-24 05:16:20 +02:00
report_context['last_purchase'] = last_purchase
2021-08-26 20:16:55 +02:00
report_context['location'] = data['location_names']
report_context['stock_date_end'] = data['to_date']
report_context['company'] = Company(data['company'])
return report_context
class PrintProductsStart(ModelView):
'Products Start'
__name__ = 'stock_co.print_products.start'
company = fields.Many2One('company.company', 'Company', required=True)
category = fields.Many2One('product.category', 'Category')
@staticmethod
def default_company():
return Transaction().context.get('company')
class PrintProducts(Wizard):
'Warehouse Stock'
__name__ = 'stock_co.print_products'
start = StateView('stock_co.print_products.start',
'stock_co.print_products_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('stock_co.print_products.report')
def do_print_(self, action):
category_id = None
if self.start.category:
category_id = self.start.category.id
data = {
'company': self.start.company.id,
'category': category_id,
}
return action, data
class PrintProductsReport(Report):
'Warehouse Report'
__name__ = 'stock_co.print_products.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
Product = pool.get('product.product')
Date_ = Pool().get('ir.date')
dom_products = [
('active', '=', True),
('type', '=', 'goods'),
('consumable', '=', False),
]
if data['category']:
dom_products.append(('account_category', '=', data['category']))
products = Product.search(dom_products, order=[('code', 'ASC')])
report_context['records'] = products
report_context['stock_date_end'] = Date_.today()
report_context['company'] = Company(data['company'])
return report_context
2021-08-28 18:27:55 +02:00
class WarehouseStockDetailedStart(ModelView):
'Warehouse Stock Detailed Start'
__name__ = 'stock_co.warehouse_stock_detailed.start'
company = fields.Many2One('company.company', 'Company', required=True)
locations = fields.Many2Many('stock.location', None, None, "Locations",
domain=[
('id', 'in', Eval('location_storage')),
('active', '=', True)
], depends=['location_storage'])
location_storage = fields.One2Many('stock.location', None, 'Locations Storage')
to_date = fields.Date('To Date', required=True)
2022-04-18 20:15:11 +02:00
lot = fields.Boolean('Grouping Lot')
2021-08-28 18:27:55 +02:00
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_to_date():
Date_ = Pool().get('ir.date')
return Date_.today()
@staticmethod
def default_location_storage():
Location = Pool().get('stock.location')
locations = Location.search([('type', '=', 'warehouse')])
2021-12-29 21:48:04 +01:00
return [l.storage_location.id for l in locations if l.storage_location.id]
2021-08-28 18:27:55 +02:00
class WarehouseStockDetailed(Wizard):
'Warehouse Stock Detailed'
__name__ = 'stock_co.warehouse_stock_detailed'
start = StateView('stock_co.warehouse_stock_detailed.start',
'stock_co.warehouse_stock_detailed_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('stock_co.warehouse_stock_detailed.report')
def do_print_(self, action):
location_ids = None
if self.start.locations:
location_ids = [l.id for l in self.start.locations]
data = {
'company': self.start.company.id,
'locations': location_ids,
'to_date': self.start.to_date,
2022-04-18 20:15:11 +02:00
'lot': self.start.lot,
2021-08-28 18:27:55 +02:00
}
return action, data
class WarehouseStockDetailedReport(Report):
'Warehouse Stock Detailed Report'
__name__ = 'stock_co.warehouse_stock_detailed.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
Product = pool.get('product.product')
Location = pool.get('stock.location')
2022-04-18 20:15:11 +02:00
LotbyLocation = pool.get('stock.lots_by_locations')
2021-08-28 18:27:55 +02:00
if data['locations']:
ids_location = data['locations']
locations = Location.browse(ids_location)
else:
locations = Location.search([
('type', '=', 'warehouse')
])
ids_location = [l.storage_location.id for l in locations if l.storage_location]
dom_products = [
('active', '=', True),
('template.active', '=', True),
('type', '=', 'goods'),
('consumable', '=', False),
]
stock_context = {
'stock_date_end': data['to_date'],
'locations': ids_location,
}
# dom_products.append(('account_category', '=', data['category']))
2021-08-30 22:39:59 +02:00
cursor = Transaction().connection.cursor()
query = "select distinct on(p.id) p.id, t.name, p.code, t.reference, s.effective_date from product_product as p right join stock_move as s on p.id=s.product join product_template as t on p.template=t.id where s.shipment ilike 'stock.shipment.in,%' and state='done' order by p.id, s.effective_date DESC"
2021-08-30 22:39:59 +02:00
cursor.execute(query)
columns = list(cursor.description)
result = cursor.fetchall()
2021-09-24 05:16:20 +02:00
last_purchase = {}
2021-08-30 22:39:59 +02:00
for row in result:
row_dict = {}
for i, col in enumerate(columns):
row_dict[col.name] = row[i]
2021-09-24 05:16:20 +02:00
last_purchase[row[0]] = row_dict
2021-08-30 22:39:59 +02:00
2021-08-28 18:27:55 +02:00
values = []
add_ = values.append
2021-08-28 18:27:55 +02:00
for l in locations:
stock_context['locations'] = [l.id]
with Transaction().set_context(stock_context):
prdts = Product.search(dom_products, order=[('code', 'ASC')])
2022-04-18 20:15:11 +02:00
if data['lot']:
prd_ids = [p.id for p in prdts]
prdts = LotbyLocation.search([('product' , 'in', prd_ids)])
2021-08-28 18:27:55 +02:00
for p in prdts:
add_({
2021-08-28 18:27:55 +02:00
'parent_name': l.parent.name if l.parent else '',
'name': l.name,
2022-04-18 20:15:11 +02:00
'product': p.product if data['lot'] else p,
'lot': p if data['lot'] else None,
2021-08-28 18:27:55 +02:00
})
2021-08-30 22:39:59 +02:00
2021-08-28 18:27:55 +02:00
products = values
report_context['records'] = products
report_context['Decimal'] = Decimal
2021-09-24 05:16:20 +02:00
report_context['last_purchase'] = last_purchase
2021-08-28 18:27:55 +02:00
report_context['stock_date_end'] = data['to_date']
report_context['company'] = Company(data['company'])
return report_context
2021-12-29 17:41:02 +01:00
class WarehouseKardexStockStart(ModelView):
'Warehouse Kardex Stock Start'
__name__ = 'stock_co.warehouse_kardex_stock.start'
company = fields.Many2One('company.company', 'Company', required=True)
2022-02-07 23:48:18 +01:00
locations = fields.Many2Many('stock.location', None, None, "Location",
2021-12-29 17:41:02 +01:00
domain=[
('type', 'in', ['warehouse']),
('active', '=', True)
], required=True)
from_date = fields.Date('From Date', required=True)
to_date = fields.Date('To Date', required=True)
2023-10-24 21:58:28 +02:00
detail_by_product = fields.Boolean('Detail By Product')
2021-12-29 17:41:02 +01:00
categories = fields.Many2Many('product.category', None, None, 'Categories')
2023-10-24 21:58:28 +02:00
products = fields.Many2Many('product.product', None, None, 'Products',
domain=[
('active', '=', True),
('template.active', '=', True),
('type', '=', 'goods'),
('consumable', '=', False),
('quantity', '!=', 0),
],
states={'required': Eval('detail_by_product', True)},
depends=['detail_by_product'])
2021-12-29 17:41:02 +01:00
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_to_date():
Date_ = Pool().get('ir.date')
return Date_.today()
class WarehouseKardexStock(Wizard):
'Warehouse Kardex Stock'
__name__ = 'stock_co.warehouse_kardex_stock'
start = StateView('stock_co.warehouse_kardex_stock.start',
'stock_co.warehouse_kardex_stock_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('stock_co.warehouse_kardex_stock.report')
def do_print_(self, action):
data = {
'company': self.start.company.id,
2022-02-07 23:48:18 +01:00
# 'location': self.start.location.id,
2021-12-29 17:41:02 +01:00
'from_date': self.start.from_date,
'to_date': self.start.to_date,
2023-10-24 21:58:28 +02:00
'detail_by_product': self.start.detail_by_product,
2021-12-29 17:41:02 +01:00
'categories': [l.id for l in self.start.categories],
2022-02-07 23:48:18 +01:00
'locations': [l.id for l in self.start.locations],
2023-10-24 21:58:28 +02:00
'products': [l.id for l in self.start.products],
2021-12-29 17:41:02 +01:00
}
return action, data
class WarehouseKardexReport(Report):
'Warehouse Kardex Report'
__name__ = 'stock_co.warehouse_kardex_stock.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
Product = pool.get('product.product')
Location = pool.get('stock.location')
2022-02-07 23:48:18 +01:00
warehouses = Location.browse(data['locations'])
id_locations = [w.storage_location.id for w in warehouses if w.storage_location]
2021-12-29 17:41:02 +01:00
dom_products = [
('active', '=', True),
('template.active', '=', True),
('type', '=', 'goods'),
('consumable', '=', False),
2022-04-08 21:25:14 +02:00
('quantity', '!=', 0),
2021-12-29 17:41:02 +01:00
]
if data['categories']:
dom_products.append(['AND', ['OR', [
('account_category', 'in', data['categories']),
], [
('categories', 'in', data['categories']),
],
]])
stock_context_start = {
'stock_date_end': (data['from_date'] - timedelta(1)),
2022-02-07 23:48:18 +01:00
'locations': id_locations,
2021-12-29 17:41:02 +01:00
}
stock_context_end = {
'stock_date_end': data['to_date'],
2022-02-07 23:48:18 +01:00
'locations': id_locations,
2021-12-29 17:41:02 +01:00
}
2022-01-18 22:23:18 +01:00
products = {}
2023-10-28 17:36:36 +02:00
products_detail = []
2023-10-24 21:58:28 +02:00
fields_names = ['code', 'name', 'brand', 'reference', 'quantity']
2022-01-18 22:23:18 +01:00
# products = {p['id']: p for p in products_start}
# type = {'input': f'and from_location != {output_location} and to_location = {id_location}',
# 'input_dif': f'and from_location = {id_location} and to_location = {input_location}',
# 'output': f'and from_location = {id_location} and to_location != {output_location}',
# 'output_dif': f'and from_location = {output_location} and to_location = {id_location}'
# }
2022-02-07 23:48:18 +01:00
tup_locations = str(tuple(id_locations)).replace(',', '') if len(
id_locations) == 1 else str(tuple(id_locations))
2022-01-18 22:23:18 +01:00
type = {
2022-02-07 23:48:18 +01:00
'input': f'and to_location in {tup_locations}',
'output': f'and from_location in {tup_locations}',
2022-01-18 22:23:18 +01:00
}
2021-12-29 17:41:02 +01:00
moves = {}
2023-10-24 21:58:28 +02:00
if not data['detail_by_product']:
with Transaction().set_context(stock_context_start):
products_start = Product.search_read(dom_products, fields_names=fields_names, order=[('code', 'ASC')])
with Transaction().set_context(stock_context_end):
products_end = Product.search_read(dom_products, fields_names=fields_names, order=[('code', 'ASC')])
for p in products_start:
cls.set_value(p, 'start', products)
for p in products_end:
cls.set_value(p, 'end', products)
for k, v in type.items():
clause = v
2023-10-26 18:17:27 +02:00
query = f"""select s.product as id, p.code, t.name, t.reference, sum(s.quantity) as quantity from stock_move as s
join product_product as p on p.id = s.product
join product_template as t on p.template = t.id
where effective_date >= '{str(data['from_date'])}'
and effective_date <= '{str(data['to_date'])}'
{clause}
group by s.product, p.code, t.name, t.reference ;"""
moves = cls.query_to_dict(query)
for m, v in moves.items():
cls.set_value(v, k, products)
2022-01-18 22:23:18 +01:00
2023-10-24 21:58:28 +02:00
else:
2023-10-28 17:36:36 +02:00
for product in data['products']:
query = f"""select p.id as pid, p.code, t.name, pb.name as brand, t.reference, s.effective_date,
SPLIT_PART(s.origin, ',', 1) as model,
SPLIT_PART(s.origin, ',', 2) as line_id,
s.id as move_id, s.quantity as quantity
2023-10-26 18:17:27 +02:00
from stock_move as s
2023-10-28 17:36:36 +02:00
INNER JOIN product_product as p on p.id = s.product
INNER JOIN product_template as t on p.template = t.id
INNER JOIN product_brand as pb on pb.id = t.brand
2023-10-31 22:08:40 +01:00
where s.effective_date >= '{str(data['from_date'])}'
and s.effective_date <= '{str(data['to_date'])}'
2023-10-28 17:36:36 +02:00
AND p.id IN ({product})
ORDER BY s.effective_date ASC, p.id ASC
"""
2023-10-31 22:08:40 +01:00
# and (s.origin LIKE '%purchase.line%' or s.origin LIKE '%sale.line%')
2023-11-21 23:13:00 +01:00
start, end = cls.get_balances(Product, product, data['from_date'] - timedelta(days=1), id_locations)
2023-10-28 17:36:36 +02:00
products_detail.append(cls.query_to_dict_detail(query, end))
2022-02-07 23:48:18 +01:00
wh_name = ''
for l in warehouses:
2023-10-24 21:58:28 +02:00
wh_name += (l.name + ', ')
2022-01-18 22:23:18 +01:00
report_context['products'] = products.values()
2023-10-28 17:36:36 +02:00
report_context['products_detail'] = products_detail
2023-10-24 21:58:28 +02:00
report_context['detail_by_product'] = data['detail_by_product']
2022-02-07 23:48:18 +01:00
report_context['warehouse'] = wh_name
2021-12-29 17:41:02 +01:00
report_context['company'] = Company(data['company'])
return report_context
2022-01-18 22:23:18 +01:00
@classmethod
def set_value(cls, p, key, products):
defaults = {
'start': 0,
'input': 0,
'output': 0,
'end': 0
}
try:
products[p['id']].update({
key: p['quantity']
})
except:
products[p['id']] = copy.deepcopy(defaults)
products[p['id']].update({
'code': p['code'],
'name': p['name'],
'reference': p['reference'],
key: p['quantity']
})
2023-10-24 21:58:28 +02:00
@classmethod
2023-10-28 17:36:36 +02:00
def set_value_detail(cls, p, key, Product, id_locations):
2023-10-26 22:59:40 +02:00
start, end = cls.get_balances(Product, p['pid'], p['effective_date'], id_locations)
2023-10-24 21:58:28 +02:00
@classmethod
def get_balances(cls, Product, id, effective_data, locations):
dom_products = [('id', '=', id)]
fields_names = ['code', 'name', 'brand', 'reference', 'quantity']
products = {}
stock_context = {
'stock_date_end': effective_data - timedelta(1),
'locations': locations,
}
stock_context_end = {
'stock_date_end': effective_data,
'locations': locations,
}
with Transaction().set_context(stock_context):
2023-10-26 18:17:27 +02:00
products_start = Product.search_read(dom_products, fields_names=fields_names)
2023-10-24 21:58:28 +02:00
with Transaction().set_context(stock_context_end):
2023-10-26 18:17:27 +02:00
products_end = Product.search_read(dom_products, fields_names=fields_names)
2023-10-24 21:58:28 +02:00
for p in products_start:
cls.set_value(p, 'start', products)
for p in products_end:
cls.set_value(p, 'end', products)
return products[id]['start'], products[id]['end']
2021-12-29 17:41:02 +01:00
@classmethod
def query_to_dict(cls, query):
cursor = Transaction().connection.cursor()
cursor.execute(query)
columns = list(cursor.description)
result = cursor.fetchall()
res_dict = {}
for row in result:
row_dict = {}
for i, col in enumerate(columns):
row_dict[col.name] = row[i]
res_dict[row[0]] = row_dict
return res_dict
2023-10-28 17:36:36 +02:00
@classmethod
def query_to_dict_detail(cls, query, end):
pool = Pool()
SaleLine = pool.get('sale.line')
PurchaseLine = pool.get('purchase.line')
2023-10-31 22:08:40 +01:00
StockMove = pool.get('stock.move')
2023-10-28 17:36:36 +02:00
cursor = Transaction().connection.cursor()
cursor.execute(query)
result = cursor.fetchall()
sale_line_ids = []
purchase_line_ids = []
2023-10-31 22:08:40 +01:00
stock_inventory_id = []
2023-10-28 17:36:36 +02:00
for row in result:
if row[6] == 'sale.line':
sale_line_ids.append(row[7])
2023-10-31 22:08:40 +01:00
elif row[6] == 'purchase.line':
2023-10-28 17:36:36 +02:00
purchase_line_ids.append(row[7])
2023-10-31 22:08:40 +01:00
elif row[6] == 'stock.inventory':
stock_inventory_id.append(row[7])
sale_lines = SaleLine.search_read([('id', 'in', sale_line_ids)], fields_names=['sale.party.name', 'sale.number', 'unit_price', 'quantity'])
sale_lines_dict = {sl['id']: (sl['sale.']['party.']['name'], sl['sale.']['number'], sl['unit_price'], sl['quantity']) for sl in sale_lines}
purchase_lines = PurchaseLine.search_read([('id', 'in', purchase_line_ids)], fields_names=['purchase.party.name', 'purchase.number', 'unit_price', 'quantity'])
purchase_lines_dict = {pl['id']: (pl['purchase.']['party.']['name'], pl['purchase.']['number'], pl['unit_price'], pl['quantity']) for pl in purchase_lines}
2023-10-28 17:36:36 +02:00
res = []
balance = end
cost_price = 0
for row in result:
qty_input = 0
qty_output = 0
list_price = 0
party = ''
number = ''
2023-10-31 22:08:40 +01:00
add_item = False
2023-11-21 23:13:00 +01:00
print(row[8], row[0], 'origens')
2023-10-28 17:36:36 +02:00
if row[6] == 'sale.line':
2023-11-21 23:13:00 +01:00
print('paso por if')
2023-10-31 22:08:40 +01:00
party, number, list_price, qty_output = sale_lines_dict[int(row[7])]
2023-10-28 17:36:36 +02:00
balance -= qty_output
2023-10-31 22:08:40 +01:00
add_item = True
elif row[6] == 'purchase.line':
# qty_input = row[9]
2023-11-21 23:13:00 +01:00
print('paso por elif')
2023-10-31 22:08:40 +01:00
party, number, cost_price, qty_input = purchase_lines_dict[int(row[7])]
2023-10-28 17:36:36 +02:00
balance += qty_input
2023-10-31 22:08:40 +01:00
add_item = True
else:
move = StockMove(row[8])
2023-11-21 23:13:00 +01:00
print('paso por else')
2023-11-21 14:46:02 +01:00
if move and (move.from_location.type == 'lost_found' or move.from_location.type == 'customer' or move.from_location.type == 'supplier'):
2023-10-31 22:08:40 +01:00
qty_input = row[9]
balance += qty_input
add_item = True
if add_item:
value = {
'product': row[2],
'code': row[1],
'brand': row[3],
'reference': row[4],
'effective_date': row[5],
'party': party,
'number': number,
'end_balance': balance,
'input': qty_input,
'output': qty_output,
'list_price': list_price,
'cost_price': cost_price,
}
res.append(value)
2023-10-28 17:36:36 +02:00
return res