# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import copy from datetime import date, datetime, timedelta from decimal import Decimal 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, StateTransition from trytond.report import Report from trytond.transaction import Transaction from trytond.pyson import Eval, Equal, If, In 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 class Move(metaclass=PoolMeta): __name__ = 'stock.move' description = fields.Char('Description') current_stock = fields.Function(fields.Float('Current Stock', depends=['product']), 'on_change_with_current_stock') reference = fields.Function(fields.Char('Reference', depends=['product'], help='reference of product'), 'get_reference') @fields.depends('current_stock') def on_change_product(self, name=None): super(Move, self).on_change_product() self.current_stock = self.on_change_with_current_stock() @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 @fields.depends('product', 'from_location', 'to_location') def on_change_with_current_stock(self, name=None): res = 0 location = None if self.from_location and self.from_location.type == 'storage' and self.from_location.parent: location = self.from_location.parent.storage_location elif self.to_location and self.to_location.type == 'storage' and self.to_location.parent: 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( [self.product], 'quantity', [location.id], grouping_filter=([self.product.id],) ) if res_dict.get(self.product.id): res += res_dict[self.product.id] return res @fields.depends('product') def on_change_with_description(self, name=None): res = '' if self.product and self.product.description: description = self.product.description res = description.rstrip('\n') return res @classmethod def do(cls, moves): super(Move, cls).do(moves) for move in moves: product = move.product if not move.description: move.description = cls.on_change_with_description(move) move.save() # remove origin 'stock.inventory.line' for set average cost if move.origin and move.origin.__name__ in ('purchase.line'): if product.cost_price_method == 'fixed': product.cost_price = move.unit_price product.save() move.set_average_cost() if hasattr(product, 'standard_margin') and product.standard_margin and product.standard_margin > 0 and move.product.cost_price > 0: template = product.template percentage_calculation = round(move.product.cost_price * Decimal(move.product.standard_margin/100), 4) template.list_price = product.cost_price + percentage_calculation template.on_change_list_price() template.save() 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]) 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') @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: 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') 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']) for move in moves: product = move.product amount = move.quantity * float(product.cost_price) origin = None id_dict = product.id if location.type == 'customer': id_dict = move.origin.sale.party.name + str(product.id) origin = move.origin.sale.party.name if move.origin else None try: row = products[id_dict] row[4].append(move.quantity) row[6].append(amount) 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 products[id_dict] = [ product.code, origin, product.rec_name, template.default_uom.symbol, [move.quantity], product.cost_price, [amount], category, brand, template.list_price, effective_date, ] report_context['records'] = products.values() report_context['from_location'] = from_location report_context['to_location'] = location report_context['customer_location'] = location.type == 'customer' 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') group_by_supplier = fields.Boolean('Group By Supplier') category = fields.Many2One('product.category', 'Category') suppliers = fields.Many2Many('party.party', None, None, "Suppliers", domain=[ ('active', '=', True) ]) zero_quantity = fields.Boolean('Zero Quantity') @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, 'group_by_supplier': self.start.group_by_supplier, 'zero_quantity': self.start.zero_quantity, 'to_date': self.start.to_date, 'category': category_id, 'suppliers': [l.id for l in self.start.suppliers], } 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']) Move = pool.get('stock.move') 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']]), ], ]]) if not data['zero_quantity']: 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)), ('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}) products_ids = min_quantities.keys() dom_products.append(('id', 'in', products_ids)) if data['suppliers']: dom_products.append([('template.product_suppliers.party', 'in', data['suppliers'])]) total_amount = 0 total_amount_imp = 0 values = {} products = [] if data['group_by_location']: for l in locations: days = [] stock_context['locations'] = [l.id] with Transaction().set_context(stock_context): prdts = Product.search(dom_products, order=[('code', 'ASC')]) suppliers = {} for p in prdts: move = Move.search([ ('to_location', '=', l.id), ('product', '=', p.id), ('state', '=', 'done'), ], order=[('effective_date', 'DESC')]) date_start = move[0].effective_date if len(move) > 0 else None date_now = date.today() date_result = date_now - date_start if date_start else 0 days.append(date_result.days) if date_start else days.append('error') if data['group_by_supplier']: for p in prdts: if not p.template.product_suppliers: continue for prod_sup in p.template.product_suppliers: sup_id = prod_sup.party.id try: suppliers[sup_id]['products'].append(p) 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) except: suppliers[sup_id] = {} suppliers[sup_id]['products'] = [p] suppliers[sup_id]['party'] = prod_sup.party 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] 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]) values[l.id] = { 'name': l.name, 'products': prdts, 'days': days, 'suppliers': suppliers.values(), 'total_amount': total_amount, 'total_amount_imp': total_amount_imp } products = values.values() else: with Transaction().set_context(stock_context): products = Product.search(dom_products, order=[('code', 'ASC')]) if data['only_minimal_level']: products = [p for p in products if p.quantity <= min_quantities[p.id]] 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]) suppliers = {} if data['group_by_supplier']: for p in products: if not p.template.product_suppliers: continue for prod_sup in p.template.product_suppliers: sup_id = prod_sup.party.id try: suppliers[sup_id]['products'].append(p) 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) except: suppliers[sup_id] = {} suppliers[sup_id]['products'] = [p] suppliers[sup_id]['party'] = prod_sup.party 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] products = suppliers.values() cursor = Transaction().connection.cursor() # 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']) 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;" cursor.execute(query) columns = list(cursor.description) result = cursor.fetchall() last_purchase = {} for row in result: row_dict = {} for i, col in enumerate(columns): row_dict[col.name] = row[i] last_purchase[row[0]] = row_dict report_context['group_by_location'] = data['group_by_location'] report_context['group_by_supplier'] = data['group_by_supplier'] report_context['records'] = products report_context['total_amount'] = total_amount report_context['total_amount_imp'] = total_amount_imp report_context['last_purchase'] = last_purchase 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 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) lot = fields.Boolean('Grouping Lot') @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')]) return [l.storage_location.id for l in locations if l.storage_location.id] 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, 'lot': self.start.lot, } 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') LotbyLocation = pool.get('stock.lots_by_locations') 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'])) 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" cursor.execute(query) columns = list(cursor.description) result = cursor.fetchall() last_purchase = {} for row in result: row_dict = {} for i, col in enumerate(columns): row_dict[col.name] = row[i] last_purchase[row[0]] = row_dict values = [] add_ = values.append for l in locations: stock_context['locations'] = [l.id] with Transaction().set_context(stock_context): prdts = Product.search(dom_products, order=[('code', 'ASC')]) if data['lot']: prd_ids = [p.id for p in prdts] prdts = LotbyLocation.search([('product' , 'in', prd_ids)]) for p in prdts: add_({ 'parent_name': l.parent.name if l.parent else '', 'name': l.name, 'product': p.product if data['lot'] else p, 'lot': p if data['lot'] else None, }) products = values report_context['records'] = products report_context['Decimal'] = Decimal report_context['last_purchase'] = last_purchase report_context['stock_date_end'] = data['to_date'] report_context['company'] = Company(data['company']) return report_context class WarehouseKardexStockStart(ModelView): 'Warehouse Kardex Stock Start' __name__ = 'stock_co.warehouse_kardex_stock.start' company = fields.Many2One('company.company', 'Company', required=True) locations = fields.Many2Many('stock.location', None, None, "Location", domain=[ ('type', 'in', ['warehouse']), ('active', '=', True) ], required=True) from_date = fields.Date('From Date', required=True) to_date = fields.Date('To Date', required=True) detail_by_product = fields.Boolean('Detail By Product') categories = fields.Many2Many('product.category', None, None, 'Categories') 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']) @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, # 'location': self.start.location.id, 'from_date': self.start.from_date, 'to_date': self.start.to_date, 'detail_by_product': self.start.detail_by_product, 'categories': [l.id for l in self.start.categories], 'locations': [l.id for l in self.start.locations], 'products': [l.id for l in self.start.products], } 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') warehouses = Location.browse(data['locations']) id_locations = [w.storage_location.id for w in warehouses if w.storage_location] dom_products = [ ('active', '=', True), ('template.active', '=', True), ('type', '=', 'goods'), ('consumable', '=', False), ('quantity', '!=', 0), ] 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)), 'locations': id_locations, } stock_context_end = { 'stock_date_end': data['to_date'], 'locations': id_locations, } products = {} products_detail = [] fields_names = ['code', 'name', 'brand', 'reference', 'quantity'] # 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}' # } tup_locations = str(tuple(id_locations)).replace(',', '') if len( id_locations) == 1 else str(tuple(id_locations)) type = { 'input': f'and to_location in {tup_locations}', 'output': f'and from_location in {tup_locations}', } moves = {} 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 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) else: 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 from stock_move as s 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 where s.effective_date >= '{str(data['from_date'])}' and s.effective_date <= '{str(data['to_date'])}' AND p.id IN ({product}) ORDER BY s.effective_date ASC, p.id ASC """ # and (s.origin LIKE '%purchase.line%' or s.origin LIKE '%sale.line%') start, end = cls.get_balances(Product, product, data['from_date'] - timedelta(days=1), id_locations) products_detail.append(cls.query_to_dict_detail(query, end)) wh_name = '' for l in warehouses: wh_name += (l.name + ', ') report_context['products'] = products.values() report_context['products_detail'] = products_detail report_context['detail_by_product'] = data['detail_by_product'] report_context['warehouse'] = wh_name report_context['company'] = Company(data['company']) return report_context @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'] }) @classmethod def set_value_detail(cls, p, key, Product, id_locations): start, end = cls.get_balances(Product, p['pid'], p['effective_date'], id_locations) @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): products_start = Product.search_read(dom_products, fields_names=fields_names) with Transaction().set_context(stock_context_end): products_end = Product.search_read(dom_products, fields_names=fields_names) 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'] @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 @classmethod def query_to_dict_detail(cls, query, end): pool = Pool() SaleLine = pool.get('sale.line') PurchaseLine = pool.get('purchase.line') StockMove = pool.get('stock.move') cursor = Transaction().connection.cursor() cursor.execute(query) result = cursor.fetchall() sale_line_ids = [] purchase_line_ids = [] stock_inventory_id = [] for row in result: if row[6] == 'sale.line': sale_line_ids.append(row[7]) elif row[6] == 'purchase.line': purchase_line_ids.append(row[7]) 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} res = [] balance = end cost_price = 0 for row in result: qty_input = 0 qty_output = 0 list_price = 0 party = '' number = '' add_item = False print(row[8], row[0], 'origens') if row[6] == 'sale.line': print('paso por if') party, number, list_price, qty_output = sale_lines_dict[int(row[7])] balance -= qty_output add_item = True elif row[6] == 'purchase.line': # qty_input = row[9] party, number, cost_price, qty_input = purchase_lines_dict[int(row[7])] balance += qty_input add_item = True else: move = StockMove(row[8]) print('paso por else') if move and (move.from_location.type == 'lost_found' or move.from_location.type == 'customer' or move.from_location.type == 'supplier'): 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) return res class CreateOrderPointStart(ModelView): 'Create Order Point Start' __name__ = 'stock_co.create_order_point.start' category = fields.Many2One('product.category', 'Category', required=True, domain=[('accounting', '=', False)]) warehouse_location = fields.Many2One('stock.location', 'Warehouse Location', select=True, domain=[('type', '=', 'warehouse')], states={ 'invisible': Not(Equal(Eval('type'), 'purchase')), 'required': Equal(Eval('type'), 'purchase'), }, depends=['type']) storage_location = fields.Many2One('stock.location', 'Storage Location', select=True, domain=[('type', '=', 'storage')], states={ 'invisible': Not(Equal(Eval('type'), 'internal')), 'required': Equal(Eval('type'), 'internal'), }, depends=['type']) location = fields.Function(fields.Many2One('stock.location', 'Location'), 'get_location', searcher='search_location') provisioning_location = fields.Many2One( 'stock.location', 'Provisioning Location', domain=[('type', 'in', ['storage', 'view'])], states={ 'invisible': Not(Equal(Eval('type'), 'internal')), 'required': ((Eval('type') == 'internal') & (Eval('min_quantity', None) != None)), # noqa: E711 }, depends=['type', 'min_quantity']) overflowing_location = fields.Many2One( 'stock.location', 'Overflowing Location', domain=[('type', 'in', ['storage', 'view'])], states={ 'invisible': Eval('type') != 'internal', 'required': ((Eval('type') == 'internal') & (Eval('max_quantity', None) != None)), # noqa: E711 }, depends=['type', 'max_quantity']) type = fields.Selection( [('internal', 'Internal'), ('purchase', 'Purchase')], 'Type', select=True, required=True) min_quantity = fields.Float('Minimal Quantity', digits=(16, Eval('unit_digits', 2)), states={ # required for purchase and production types 'required': Eval('type') != 'internal', }, domain=['OR', ('min_quantity', '=', None), ('min_quantity', '<=', Eval('target_quantity', 0)), ], depends=['unit_digits', 'target_quantity', 'type']) target_quantity = fields.Float('Target Quantity', required=True, digits=(16, Eval('unit_digits', 2)), domain=[ ['OR', ('min_quantity', '=', None), ('target_quantity', '>=', Eval('min_quantity', 0)), ], ['OR', ('max_quantity', '=', None), ('target_quantity', '<=', Eval('max_quantity', 0)), ], ], depends=['unit_digits', 'min_quantity', 'max_quantity']) max_quantity = fields.Float('Maximal Quantity', digits=(16, Eval('unit_digits', 2)), states={ 'invisible': Eval('type') != 'internal', }, domain=['OR', ('max_quantity', '=', None), ('max_quantity', '>=', Eval('target_quantity', 0)), ], depends=['unit_digits', 'type', 'target_quantity']) company = fields.Many2One('company.company', 'Company', required=True, domain=[ ('id', If(In('company', Eval('context', {})), '=', '!='), Eval('context', {}).get('company', -1)), ]) unit = fields.Function(fields.Many2One('product.uom', 'Unit'), 'get_unit') unit_digits = fields.Function(fields.Integer('Unit Digits'), 'get_unit_digits') @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_type(): return "purchase" class CreateOrderPoint(Wizard): 'Create Order Point' __name__ = 'stock_co.create_order_point' start = StateView('stock_co.create_order_point.start', 'stock_co.create_order_point_start_view_form', [ Button('Cancel', 'end', 'tryton-cancel'), Button('Ok', 'accept', 'tryton-ok', default=True), ]) accept = StateTransition() def transition_accept(self): pool = Pool() OrderPoint = pool.get('stock.order_point') Product = pool.get('product.product') order_points = [] type = self.start.type company = self.start.company warehouse_location = self.start.warehouse_location min_quantity = self.start.min_quantity target_quantity = self.start.target_quantity products = Product.search([('categories', 'in', [self.start.category])]) print(products) for product in products: order_points.append({ 'type': type, 'company': company, 'warehouse_location': warehouse_location.id, 'product': product.id, 'min_quantity': min_quantity, 'target_quantity': target_quantity, }) OrderPoint.create(order_points) return 'end'