diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2024-11-11 14:11:42 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2024-11-11 14:11:42 +0700 |
| commit | fd6b9b5395b04135d9f0afdc0859fb4f07d280b4 (patch) | |
| tree | be6e7a4f201b949a3b06e0c020c668ba2b0730fa | |
| parent | f787f06a341f1775151d98be4490fe6126c511cd (diff) | |
| parent | 9a0fd25e54491bd14a5b29b62b31a440dfa1bebc (diff) | |
Merge branch 'production' into feature/max_plafon_order_qty
| -rw-r--r-- | indoteknik_api/controllers/api_v1/stock_picking.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/approval_date_doc.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/delivery_order.py | 4 | ||||
| -rwxr-xr-x | indoteknik_custom/models/product_template.py | 49 | ||||
| -rwxr-xr-x | indoteknik_custom/models/purchase_order.py | 12 | ||||
| -rw-r--r-- | indoteknik_custom/models/requisition.py | 55 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/sale_order_line.py | 11 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 33 | ||||
| -rw-r--r-- | indoteknik_custom/models/website_user_cart.py | 41 | ||||
| -rw-r--r-- | indoteknik_custom/views/requisition.xml | 7 |
11 files changed, 146 insertions, 71 deletions
diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index f0c7456d..ea8c6400 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -123,7 +123,7 @@ class StockPicking(controller.Controller): params = {'sj_documentation': sj_document, 'paket_documentation': paket_document, - 'driver_arrival_date': self.time_to_str(datetime.utcnow(), '%Y-%m-%d %H:%M:%S'), + 'driver_arrival_date': datetime.utcnow(), } picking_data = request.env['stock.picking'].search([('picking_code', '=', picking_code)], limit=1) diff --git a/indoteknik_custom/models/approval_date_doc.py b/indoteknik_custom/models/approval_date_doc.py index 441ada3d..751bae82 100644 --- a/indoteknik_custom/models/approval_date_doc.py +++ b/indoteknik_custom/models/approval_date_doc.py @@ -40,6 +40,7 @@ class ApprovalDateDoc(models.Model): raise UserError("Hanya Accounting Yang Bisa Approve") self.check_invoice_so_picking self.picking_id.driver_departure_date = self.driver_departure_date + self.picking_id.date_doc_kirim = self.driver_departure_date self.state = 'done' self.approve_date = datetime.utcnow() self.approve_by = self.env.user.id diff --git a/indoteknik_custom/models/delivery_order.py b/indoteknik_custom/models/delivery_order.py index 2ed49a54..3473197b 100644 --- a/indoteknik_custom/models/delivery_order.py +++ b/indoteknik_custom/models/delivery_order.py @@ -33,11 +33,13 @@ class DeliveryOrder(models.TransientModel): picking.driver_id = self.env.uid picking.delivery_tracking_no = line_tracking_no + if picking.driver_departure_date: + picking.sj_return_date = datetime.utcnow() + delivery_type = self.env['delivery.order.line'].get_delivery_type(picking.driver_departure_date, picking.driver_arrival_date) if delivery_type == 'departure': picking.driver_departure_date = current_time elif delivery_type == 'arrival': - picking.sj_return_date = datetime.utcnow() sale_order = False if picking.origin: diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index d3343e75..3bb06eaf 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -391,23 +391,46 @@ class ProductProduct(models.Model): @api.constrains('active') def archive_product(self): for product in self: + if self.env.context.get('skip_unpublished_constraint'): + continue # Mencegah looping saat dipanggil dari metode lain + product_template = product.product_tmpl_id variants = product_template.product_variant_ids - if product_template.active and product.active: - if not product.active and len(variants) == 1: - product_template.with_context(skip_active_constraint=True).active = False - product_template.unpublished = True - elif not product.active and len(variants) > 1: + if len(variants) == 1: + # Jika hanya ada satu varian, atur status `unpublished` berdasarkan `active` + product_template.with_context(skip_unpublished_constraint=True).unpublished = not product.active + product.with_context(skip_unpublished_constraint=True).unpublished = not product.active + else: + if product.active: + product.with_context(skip_unpublished_constraint=True).unpublished = False + product_template.with_context(skip_unpublished_constraint=True).unpublished = any(variant.active for variant in variants) + else: + product.with_context(skip_unpublished_constraint=True).unpublished = True all_inactive = all(not variant.active for variant in variants) - if all_inactive: - product_template.with_context(skip_active_constraint=True).active = False - product_template.unpublished = True - else: - continue - if any(variant.active for variant in variants): - product_template.unpublished = False - variants.unpublished = False + product_template.with_context(skip_unpublished_constraint=True).unpublished = all_inactive + + @api.constrains('unpublished') + def archive_product_unpublished(self): + for product in self: + if self.env.context.get('skip_active_constraint'): + continue # Mencegah looping saat dipanggil dari metode lain + + product_template = product.product_tmpl_id + variants = product_template.product_variant_ids + + if len(variants) == 1: + # Jika hanya ada satu varian, atur status `unpublished` pada template, tetapi biarkan `active` tetap True + product_template.with_context(skip_active_constraint=True).unpublished = product.unpublished + else: + if not product.unpublished: + # Jika `unpublished` adalah False, pastikan `active` tetap True + product.with_context(skip_active_constraint=True).active = True + product_template.with_context(skip_active_constraint=True).active = any(not variant.unpublished for variant in variants) + else: + # Jika `unpublished` adalah True, atur template hanya jika semua varian di-unpublished + all_unpublished = all(variant.unpublished for variant in variants) + product_template.with_context(skip_active_constraint=True).active = not all_unpublished def update_internal_reference_variants(self, limit=100): variants = self.env['product.product'].search([ diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 231acb4c..f5b5979b 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -605,7 +605,7 @@ class PurchaseOrder(models.Model): if self.total_percent_margin < self.total_so_percent_margin and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and not self.env.user.is_leader: raise UserError("Beda Margin dengan Sales, harus approval Merchandise") if not self.from_apo: - if not self.sale_order_id and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and not self.env.user.is_leader: + if not self.matches_so and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and not self.env.user.is_leader: raise UserError("Tidak ada link dengan SO, harus approval Merchandise") send_email = False @@ -738,7 +738,7 @@ class PurchaseOrder(models.Model): raise UserError("Hanya Merchandiser yang bisa approve") if self.env.user.is_leader or self.env.user.has_group('indoteknik_custom.group_role_merchandiser'): raise UserError("Bisa langsung Confirm") - elif self.total_percent_margin == self.total_so_percent_margin and self.sale_order_id: + elif self.total_percent_margin == self.total_so_percent_margin and self.matches_so: raise UserError("Bisa langsung Confirm") else: reason = '' @@ -747,12 +747,14 @@ class PurchaseOrder(models.Model): reason = 'above 50jt, ' if self.total_percent_margin < self.total_so_percent_margin: reason += 'diff margin, ' - if not self.from_apo and not self.sale_order_id: - reason += 'not link with sales, ' + if not self.from_apo and not self.matches_so: + reason += 'not link with pj and reorder, ' + if not self.matches_so: + reason += 'not link with so, ' # Post a highlighted message to lognote self.message_post( body=f"<div style='background-color: #fdf2e9; border: 1px solid #f5c6cb; padding: 10px;'>" - f"<b>Note Return (Pinned):</b><br>{reason}</div>", + f"<b>Note (Pinned):</b><br>{reason}</div>", subtype_id=self.env.ref("mail.mt_note").id ) diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py index c4104ec5..704ae8c0 100644 --- a/indoteknik_custom/models/requisition.py +++ b/indoteknik_custom/models/requisition.py @@ -1,4 +1,4 @@ -from odoo import models, fields, api, _ +from odoo import models, fields, api, tools, _ from odoo.exceptions import UserError from datetime import datetime import math @@ -7,6 +7,33 @@ import logging _logger = logging.getLogger(__name__) +class RequisitionMatchPO(models.Model): + _name = 'v.requisition.match.po' + _auto = False + _rec_name = 'purchase_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' @@ -22,19 +49,36 @@ class Requisition(models.Model): 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', domain="[('state', '=', 'sale')]") + sales_approve = fields.Boolean(string='Sales Approve', tracking=3, copy=False) + merchandise_approve = fields.Boolean(string='Merchandise Approve', tracking=3, copy=False) @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 button_approve(self): + if self.env.user.id not in [377, 19]: + raise UserError('Hanya Vita dan Darren Yang Bisa Approve') + if self.env.user.id == 377: + self.sales_approve = True + elif self.env.user.id == 19: + if not self.sales_approve: + raise UserError('Vita Belum Approve') + self.merchandise_approve = True def create_po_from_requisition(self): + if not self.sales_approve: + raise UserError('Harus di Approve Vita') + if not self.merchandise_approve: + raise UserError('Harus di Approve Darren') 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 self.sale_order_id: + 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), @@ -108,6 +152,13 @@ class Requisition(models.Model): 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): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8e170b1c..5545e28c 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -78,7 +78,7 @@ class SaleOrder(models.Model): payment_link_midtrans = fields.Char(string='Payment Link', help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri') payment_qr_code = fields.Binary("Payment QR Code") due_id = fields.Many2one('due.extension', string="Due Extension", readonly=True, tracking=True) - vendor_approval_id = fields.Many2one('vendor.approval', string="Vendor Approval", readonly=True, tracking=True) + vendor_approval_id = fields.Many2one('vendor.approval', string="Vendor Approval", readonly=True, tracking=True, copy=False) customer_type = fields.Selection([ ('pkp', 'PKP'), ('nonpkp', 'Non PKP') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 5e01067a..978b1f69 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -251,12 +251,9 @@ class SaleOrderLine(models.Model): # purchase_price = self.env['purchase.pricelist'].search( # query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') price, taxes, vendor_id = self._get_purchase_price(line.product_id) - line.vendor_md_id = vendor_id line.vendor_id = vendor_id - line.margin_md = line.item_percent_margin line.tax_id = line.order_id.sales_tax_id # price, taxes = line._get_valid_purchase_price(purchase_price) - line.purchase_price_md = price line.purchase_price = price line.purchase_tax_id = taxes @@ -270,6 +267,14 @@ class SaleOrderLine(models.Model): line.name = line_name line.weight = line.product_id.weight + @api.constrains('vendor_id') + def _check_vendor_id(self): + for line in self: + price, taxes, vendor_id = self._get_purchase_price(line.product_id) + line.vendor_md_id = vendor_id if vendor_id else None + line.margin_md = line.item_percent_margin + line.purchase_price_md = price + def compute_delivery_amt_line(self): for line in self: try: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4c9d7658..1906dae0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -192,9 +192,9 @@ class StockPicking(models.Model): else: raise UserError(f"Error saat mengirim ke Biteship: {response.content}") - @api.constrains('driver_departure_date') - def constrains_driver_departure_date(self): - self.date_doc_kirim = self.driver_departure_date + # @api.constrains('driver_departure_date') + # def constrains_driver_departure_date(self): + # self.date_doc_kirim = self.driver_departure_date @api.constrains('arrival_time') def constrains_arrival_time(self): @@ -246,25 +246,22 @@ class StockPicking(models.Model): # break def check_state_reserve(self): - picking = self.search([ + pickings = self.search([ ('state', 'not in', ['cancel', 'draft', 'done']), ('picking_type_code', '=', 'outgoing') - ]) + ]) - for data in picking: - fullfilment = self.env['sales.order.fullfillment'].search([ - ('sales_order_id', '=', data.sale_id.id) + for picking in pickings: + fullfillments = self.env['sales.order.fullfillment'].search([ + ('sales_order_id', '=', picking.sale_id.id) ]) - - data.state_reserve = 'ready' - if not data.date_reserved: - data.date_reserved = datetime.datetime.utcnow() - - for rec in fullfilment: - if rec.reserved_from not in ['Inventory On Hand', 'Reserved from stock', 'Free Stock']: - data.state_reserve = 'waiting' - data.date_reserved = '' - break + + picking.state_reserve = 'ready' + picking.date_reserved = picking.date_reserved or datetime.datetime.utcnow() + + if any(rec.reserved_from not in ['Inventory On Hand', 'Reserved from stock', 'Free Stock'] for rec in fullfillments): + picking.state_reserve = 'waiting' + picking.date_reserved = '' def _create_approval_notification(self, approval_role): title = 'Warning' diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py index fbcb0aa4..6cb282f8 100644 --- a/indoteknik_custom/models/website_user_cart.py +++ b/indoteknik_custom/models/website_user_cart.py @@ -93,45 +93,32 @@ class WebsiteUserCart(models.Model): def get_product_by_user(self, user_id, selected=False, source=False): user_id = int(user_id) - + if source == 'buy': source = ['buy'] else: source = ['add_to_cart', 'buy'] parameters = [ - ('user_id', '=', user_id), + ('user_id', '=', user_id), ('source', 'in', source) ] - - if selected: - parameters.append(('is_selected', '=', True)) - carts = self.search(parameters) - products_active = [] - products_inactive = [] + for cart in carts: if cart.product_id: price = cart.product_id._v2_get_website_price_include_tax() - if cart.product_id.active and price > 0: - product = cart.with_context(price_for="web").get_products() - for product_active in product: - products_active.append(product_active) - else: - product_inactives = cart.with_context(price_for="web").get_products() - for inactives in product_inactives: - products_inactive.append(inactives) - else: - program = cart.with_context(price_for="web").get_products() - for programs in program: - products_active.append(programs) - data = { - 'product_total': self.search_count(parameters), - 'products': products_active, - 'products_inactive': products_inactive - } - products = carts.get_products() - return products_active + if not cart.product_id.active or price < 1: + cart.is_selected = False + + if selected: + parameters.append(('is_selected', '=', True)) + + products_active = self.search(parameters) + + products = products_active.get_products() + + return products def get_user_checkout(self, user_id, voucher=False, voucher_shipping=False, source=False): products = self.get_product_by_user(user_id=user_id, selected=True, source=source) diff --git a/indoteknik_custom/views/requisition.xml b/indoteknik_custom/views/requisition.xml index b704baaf..a866690d 100644 --- a/indoteknik_custom/views/requisition.xml +++ b/indoteknik_custom/views/requisition.xml @@ -50,6 +50,13 @@ <field name="model">requisition</field> <field name="arch" type="xml"> <form> + <header> + <button name="button_approve" + string="Approve" + type="object" + class="mr-2 oe_highlight" + /> + </header> <sheet string="Requisition"> <div class="oe_button_box" name="button_box"/> <group> |
