from odoo import models, fields, api, tools, _ from odoo.exceptions import UserError from datetime import datetime import math import logging _logger = logging.getLogger(__name__) class RequisitionMatchPO(models.Model): _name = 'v.requisition.match.po' _auto = False _rec_name = 'product_id' id = fields.Integer(string='ID') product_id = fields.Many2one('product.product', string='Product') qty_rpo = fields.Float(string='Qty RPO', help='Qty RPO yang sudah di PO namun SO masih Draft') def init(self): tools.drop_view_if_exists(self.env.cr, self._table) self.env.cr.execute(""" create or replace view %s as select rl.product_id as id, rl.product_id, sum(rl.qty_purchase) as qty_rpo from requisition_line rl join requisition r on r.id = rl.requisition_id join requisition_purchase_match rpm on rpm.requisition_id = r.id join purchase_order po on po.id = rpm.order_id join sale_order so on so.id = r.sale_order_id where 1=1 and r.date_doc >= '2024-11-11' and po.state in ('done', 'purchase') and so.state in ('draft', 'sent') group by rl.product_id """ % self._table) class Requisition(models.Model): _name = 'requisition' _order = 'id desc' _inherit = ['mail.thread'] _rec_name = 'number' number = fields.Char(string='Document No', index=True, copy=False, readonly=True) date_doc = fields.Date(string='Date', help='isi tanggal hari ini') description = fields.Char(string='Description', help='bebas isinya apa aja') requisition_lines = fields.One2many('requisition.line', 'requisition_id', string='Lines', auto_join=True) notification = fields.Char(string='Notification') is_po = fields.Boolean(string='Is PO') requisition_match = fields.One2many('requisition.purchase.match', 'requisition_id', string='Matches', auto_join=True) sale_order_id = fields.Many2one('sale.order', string='SO', help='harus diisi nomor SO yang ingin digenerate') sales_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False) merchandise_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False) def generate_requisition_from_so(self): state = ['done', 'sale'] if not self.sale_order_id: raise UserError('Sale Order Wajib Diisi dan Harus Draft') if self.sale_order_id.state in state: raise UserError('SO sudah Confirm, akan berakibat double Purchase melalui PJ') if not self.sale_order_id.order_line: raise UserError('Line SO masih kosong, harus diisi dulu') for order_line in self.sale_order_id.order_line: param = { 'requisition_id': self.id, 'product_id': order_line.product_id.id, 'partner_id': order_line.vendor_id.id, 'qty_purchase': order_line.product_uom_qty, 'price_unit': order_line.purchase_price, 'taxes_id': order_line.purchase_tax_id.id, 'subtotal': order_line.purchase_price * order_line.product_uom_qty, 'brand_id': order_line.product_id.x_manufacture.id } self.env['requisition.line'].create([param]) @api.model def create(self, vals): vals['number'] = self.env['ir.sequence'].next_by_code('requisition') or '0' result = super(Requisition, self).create(vals) return result def check_product_line_to_so(self): for req in self: for prod_line in req.requisition_lines: match = self.env['sale.order.line'].search([ ('product_id', '=', prod_line.product_id.id), ('order_id', '=', req.sale_order_id.id) ]) if match: raise UserError ('Product sudah ada di SO tidak bisa approve/create PO') def button_approve(self): state = ['done', 'sale'] # self.check_product_line_to_so() if self.sale_order_id.state in state: raise UserError('SO sudah Confirm, akan berakibat double Purchase melalui PJ') if self.env.user.id not in [21, 19, 28]: raise UserError('Hanya Rafly dan Darren Yang Bisa Approve') if self.env.user.id == 19 or self.env.user.id == 28: self.sales_approve = True elif self.env.user.id == 21 or self.env.user.id == 28: self.merchandise_approve = True def create_po_from_requisition(self): # self.check_product_line_to_so() if not (self.sales_approve or self.merchandise_approve): raise UserError('Tidak bisa create PO karena belukm diapprove oleh Darren atau Rafly') if not self.requisition_lines: raise UserError('Tidak ada Lines, belum bisa create PO') if self.is_po: raise UserError('Sudah pernah di create PO') if not self.sale_order_id and not (self.sales_approve or self.merchandise_approve): raise UserError( 'Tidak ada link dengan Sales Order, tidak bisa dihitung sebagai Plafon Qty di PO' ) vendor_ids = self.env['requisition.line'].read_group([ ('requisition_id', '=', self.id), ('partner_id', '!=', False) ], fields=['partner_id'], groupby=['partner_id']) po_ids = [] for vendor in vendor_ids: result_po = self.create_po_by_vendor(vendor['partner_id'][0]) po_ids += result_po return { 'name': _('Purchase Order'), 'view_mode': 'tree,form', 'res_model': 'purchase.order', 'target': 'current', 'type': 'ir.actions.act_window', 'domain': [('id', 'in', po_ids)], } def create_po_by_vendor(self, vendor_id): current_time = datetime.now() PRODUCT_PER_PO = 20 requisition_line = self.env['requisition.line'] param_header = { 'partner_id': vendor_id, # 'partner_ref': self.sale_order_id.name, 'currency_id': 12, 'user_id': self.env.user.id, 'company_id': 1, # indoteknik dotcom gemilang 'picking_type_id': 28, # indoteknik bandengan receipts 'date_order': current_time, 'sale_order_id': self.sale_order_id.id, 'note_description': 'from Purchase Requisition' } domain = [ ('requisition_id', '=', self.id), ('partner_id', '=', vendor_id), ('qty_purchase', '>', 0) ] products_len = requisition_line.search_count(domain) page = math.ceil(products_len / PRODUCT_PER_PO) po_ids = [] # i start from zero (0) for i in range(page): new_po = self.env['purchase.order'].create([param_header]) new_po.name = new_po.name + "/R/" + str(i + 1) po_ids.append(new_po.id) lines = requisition_line.search( domain, offset=i * PRODUCT_PER_PO, limit=PRODUCT_PER_PO ) tax = [22] for line in lines: product = line.product_id param_line = { 'order_id' : new_po.id, 'product_id': product.id, 'product_qty': line.qty_purchase, 'product_uom_qty': line.qty_purchase, 'name': product.display_name, 'price_unit': line.price_unit, 'taxes_id': tax, } new_po_line = self.env['purchase.order.line'].create([param_line]) line.current_po_id = new_po.id line.current_po_line_id = new_po_line.id self.env['requisition.purchase.match'].create([{ 'requisition_id': self.id, 'order_id': new_po.id }]) self.is_po = True return po_ids # def create_po_from_requisition(self): # if not self.requisition_lines: # raise UserError('Tidak ada Lines, belum bisa create PO') # if self.is_po: # raise UserError('Sudah pernah di create PO') # current_time = datetime.now() # vendor_ids = self.env['requisition.line'].read_group([('requisition_id', '=', self.id), ('partner_id', '!=', False)], fields=['partner_id'], groupby=['partner_id']) # counter_po_number = 0 # po_ids = [] # for vendor in vendor_ids: # param_header = { # 'partner_id': vendor['partner_id'][0], # # 'partner_ref': self.sale_order_id.name, # 'currency_id': 12, # 'user_id': self.env.user.id, # 'company_id': 1, # indoteknik dotcom gemilang # 'picking_type_id': 28, # indoteknik bandengan receipts # 'date_order': current_time, # 'sale_order_id': self.sale_order_id.id, # 'note_description': 'from Purchase Requisition' # } # param_requisition_line = [ # ('requisition_id', '=', self.id), # ('partner_id', '=', vendor['partner_id'][0]), # ('qty_purchase', '>', 0) # ] # # new_po = self.env['purchase.order'].create([param_header]) # products_vendors = self.env['requisition.line'].search(, order='brand_id') # count = brand_id = 0 # for product in products_vendors: # if count > 200 or brand_id != product.brand_id.id: # continue # count = 0 # counter_po_number += 1 # new_po = self.env['purchase.order'].create([param_header]) # new_po.name = new_po.name + "/R/"+str(counter_po_number) # self.env['requisition.purchase.match'].create([{ # 'requisition_id': self.id, # 'order_id': new_po.id # }]) # po_ids.append(new_po.id) # self.env.cr.commit() # # else: # # new_po = self.env['purchase.order'].create([param_header]) # brand_id = product.brand_id.id # count += 10 # # qty_available = product.product_id.qty_onhand_bandengan + product.product_id.qty_incoming_bandengan - product.product_id.outgoing_qty # # suggest = 'harus beli' # # if qty_available > product.qty_purchase: # # suggest = 'masih cukup' # tax = [22] # param_line = { # 'sequence': count, # 'product_id': product.product_id.id, # 'product_qty': product.qty_purchase, # 'product_uom_qty': product.qty_purchase, # 'price_unit': product.price_unit, # 'taxes_id': tax, # # 'qty_available_store': qty_available, # # 'suggest': suggest, # } # new_line = self.env['purchase.order.line'].create([param_line]) # if new_po: # new_line.write({ # 'order_id': new_po.id, # }) # product.current_po_id = new_po.id # product.current_po_line_id = new_line.id # _logger.info('Create PO Line %s' % product.product_id.name) # # self.notification = self.notification + ' %s' % new_po.name # self.is_po = True # if po_ids: # return { # 'name': _('Purchase Order'), # 'view_mode': 'tree,form', # 'res_model': 'purchase.order', # 'target': 'current', # 'type': 'ir.actions.act_window', # 'domain': [('id', 'in', po_ids)], # } class RequisitionLine(models.Model): _name = 'requisition.line' _description = 'Requisition Line' _order = 'requisition_id, id' _inherit = ['mail.thread'] requisition_id = fields.Many2one('requisition', string='Ref', required=True, ondelete='cascade', index=True, copy=False) brand_id = fields.Many2one('x_manufactures', string='Brand') product_id = fields.Many2one('product.product', string='Product', tracking=3,) partner_id = fields.Many2one('res.partner', string='Vendor') qty_purchase = fields.Float(string='Qty Purchase') price_unit = fields.Float(string='Price') tax_id = fields.Many2one('account.tax', help='isi tax pembelian include atau exclude', domain="[('type_tax_use', '=', 'purchase')]") subtotal = fields.Float(string='Subtotal') last_price = fields.Float(string='Last Price') last_order_id = fields.Many2one('purchase.order', string='Last Order') last_orderline_id = fields.Many2one('purchase.order.line', string='Last Order Line') taxes_id = fields.Many2one('account.tax', string='Tax') is_po = fields.Boolean(String='Is PO') current_po_id = fields.Many2one('purchase.order', string='Current') current_po_line_id = fields.Many2one('purchase.order.line', string='Current Line') source = fields.Char(string='Source', help='data harga diambil darimana') qty_available_store = fields.Float(string='Available') suggest = fields.Char(string='Suggest') def _get_valid_purchase_price(self, purchase_price): price = 0 taxes = 24 human_last_update = purchase_price.human_last_update or datetime.min system_last_update = purchase_price.system_last_update or datetime.min #if purchase_price.taxes_product_id.type_tax_use == 'purchase': price = purchase_price.product_price taxes = purchase_price.taxes_product_id.id or 24 if system_last_update > human_last_update: #if purchase_price.taxes_system_id.type_tax_use == 'purchase': price = purchase_price.system_price taxes = purchase_price.taxes_system_id.id or 24 return price, taxes @api.onchange('price_unit') def _onchange_price_unit(self): for line in self: line.subtotal = line.price_unit * line.qty_purchase @api.onchange('product_id') def _onchange_product(self): for line in self: line.brand_id = line.product_id.product_tmpl_id.x_manufacture.id purchase_pricelist = self.env['purchase.pricelist'].search([ ('product_id', '=', line.product_id.id) ],order='count_trx_po desc, count_trx_po_vendor desc', limit=1) price, taxes = line._get_valid_purchase_price(purchase_pricelist) line.price_unit = price line.taxes_id = taxes line.partner_id = purchase_pricelist.vendor_id.id @api.model def create(self, vals): record = super(RequisitionLine, self).create(vals) record._track_changes('Tambah') return record def write(self, vals): for record in self: old_values = {field: record[field] for field in vals if field in record} result = super(RequisitionLine, self).write(vals) for record in self: record._track_changes('Updated', old_values) return result def unlink(self): for record in self: record._track_changes('Hapus') return super(RequisitionLine, self).unlink() def _track_changes(self, action, old_values=None): message = f"Produk telah di-{action} :
" if action == 'Tambah': # message += f"
Product: {self.product_id.name}" message += f"Product: {self.product_id.name}
Vendor: {self.partner_id.name}
Qty: {self.qty_purchase}
Price: {self.price_unit}
Tax: {self.tax_id.name}
Subtotal: {self.subtotal}
Brand: {self.brand_id.x_name}" elif action == 'Hapus': # message += f"
Deleted Product: {self.product_id.name}" message += f"
Deleted Product: {self.product_id.name}
Vendor: {self.partner_id.name} Qty: {self.qty_purchase}
Price: {self.price_unit}
Tax: {self.tax_id.name}
Subtotal: {self.subtotal}
Brand: {self.brand_id.x_name}" else: # Updated for field, old_value in old_values.items(): new_value = self[field] if old_value != new_value: field_label = self._fields[field].string # Ambil nama label field message += f"{field_label}: {old_value} -> {new_value}
" if self.requisition_id: self.requisition_id.message_post(body=message) class RequisitionPurchaseMatch(models.Model): _name = 'requisition.purchase.match' _order = 'requisition_id, id' requisition_id = fields.Many2one('requisition', string='Ref', required=True, ondelete='cascade', index=True, copy=False) order_id = fields.Many2one('purchase.order', string='Purchase Order') vendor = fields.Char(string='Vendor', compute='_compute_info_po') total = fields.Float(string='Total', compute='_compute_info_po') def _compute_info_po(self): for match in self: match.vendor = match.order_id.partner_id.name match.total = match.order_id.amount_total