diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2025-10-08 14:55:04 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2025-10-08 14:55:04 +0700 |
| commit | c5642f4f6c4f0969475d863bee7243a83b9290dc (patch) | |
| tree | 06e8a41b7f752d2b21d9ee8b93d5bf7fd32c2a72 | |
| parent | 3eb010cbf141d02fb3ea3812024a2b678fcb9f48 (diff) | |
partial
| -rwxr-xr-x | indoteknik_custom/__manifest__.py | 1 | ||||
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/automatic_purchase.py | 161 | ||||
| -rw-r--r-- | indoteknik_custom/models/domain_apo.py | 12 | ||||
| -rw-r--r-- | indoteknik_custom/models/partial_delivery.py | 189 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 13 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_move.py | 1 | ||||
| -rwxr-xr-x | indoteknik_custom/security/ir.model.access.csv | 3 | ||||
| -rw-r--r-- | indoteknik_custom/views/domain_apo.xml | 46 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 48 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 1 |
11 files changed, 394 insertions, 83 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index b083be70..d1229ffe 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -186,6 +186,7 @@ # 'views/reimburse.xml', 'views/sj_tele.xml', 'views/close_tempo_mail_template.xml', + 'views/domain_apo.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 6dc61277..5ac4d6ca 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -160,3 +160,5 @@ from . import update_date_planned_po_wizard from . import unpaid_invoice_view from . import letter_receivable from . import sj_tele +from . import partial_delivery +from . import domain_apo diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index 83a7cb3c..d9ec17f4 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -93,7 +93,7 @@ class AutomaticPurchase(models.Model): counter = 0 for vendor in vendor_ids: - self.create_po_by_vendor(vendor['partner_id'][0]) + self.create_po_for_vendor(vendor['partner_id'][0]) # param_header = { # 'partner_id': vendor['partner_id'][0], @@ -191,95 +191,106 @@ class AutomaticPurchase(models.Model): if qty_pj > qty_outgoing_pj: raise UserError('Qty yang anda beli lebih dari qty outgoing. %s' %id_po) - def create_po_by_vendor(self, vendor_id): + def create_po_for_vendor(self, vendor_id): current_time = datetime.now() name = "/PJ/" if not self.apo_type == 'reordering' else "/A/" - PRODUCT_PER_PO = 20 auto_purchase_line = self.env['automatic.purchase.line'] - # Domain untuk semua baris dengan vendor_id tertentu - domain = [ + config = self.env['apo.domain.config'].search([ + ('vendor_id', '=', vendor_id) + ], limit=1) + + base_domain = [ ('automatic_purchase_id', '=', self.id), ('partner_id', '=', vendor_id), ('qty_purchase', '>', 0) ] - # Tambahkan domain khusus untuk brand_id 22 dan 564 - special_brand_domain = domain + [('brand_id', 'in', [22, 564])] - regular_domain = domain + [('brand_id', 'not in', [22, 564])] - - # Fungsi untuk membuat PO berdasarkan domain tertentu - def create_po_for_domain(domain, special_payment_term=False): - products_len = auto_purchase_line.search_count(domain) - page = math.ceil(products_len / PRODUCT_PER_PO) - - for i in range(page): - # Buat PO baru - param_header = { - 'partner_id': vendor_id, - '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, - 'from_apo': True, - 'note_description': 'Automatic PO' - } + # Kalau vendor punya brand spesial → bikin domain sesuai config + if config and config.is_special: + special_brand_domain = base_domain + [('brand_id', 'in', config.brand_ids.ids)] + self._create_po_for_domain( + vendor_id, special_brand_domain, name, PRODUCT_PER_PO, current_time, config.payment_term_id, special=config.is_special + ) - new_po = self.env['purchase.order'].create([param_header]) + # Regular domain (selain brand spesial) + regular_domain = base_domain + if config and config.is_special and config.brand_ids: + regular_domain = base_domain + [('brand_id', 'not in', config.brand_ids.ids)] - # Set payment_term_id khusus jika diperlukan - if special_payment_term: - new_po.payment_term_id = 29 - else: - new_po.payment_term_id = new_po.partner_id.property_supplier_payment_term_id + self._create_po_for_domain( + vendor_id, regular_domain, name, PRODUCT_PER_PO, current_time, config.payment_term_id + ) - new_po.name = new_po.name + name + str(i + 1) - self.env['automatic.purchase.match'].create([{ - 'automatic_purchase_id': self.id, - 'order_id': new_po.id - }]) + def _create_po_for_domain(self, vendor_id, domain, name, PRODUCT_PER_PO, current_time, payment_term_id, special=False): + auto_purchase_line = self.env['automatic.purchase.line'] + products_len = auto_purchase_line.search_count(domain) + page = math.ceil(products_len / PRODUCT_PER_PO) + + for i in range(page): + # Buat header PO + param_header = { + 'partner_id': vendor_id, + 'currency_id': 12, + 'user_id': self.env.user.id, + 'company_id': 1, + 'picking_type_id': 28, + 'date_order': current_time, + 'from_apo': True, + 'note_description': 'Automatic PO' + } + new_po = self.env['purchase.order'].create(param_header) + + # Set payment term + new_po.payment_term_id = payment_term_id.id if special else ( + new_po.partner_id.property_supplier_payment_term_id + ) + + new_po.name = new_po.name + name + str(i + 1) + + self.env['automatic.purchase.match'].create([{ + 'automatic_purchase_id': self.id, + 'order_id': new_po.id + }]) + + # Ambil lines + lines = auto_purchase_line.search( + domain, + offset=i * PRODUCT_PER_PO, + limit=PRODUCT_PER_PO + ) + + # Pre-fetch sales_match biar ga search per line + sales_matches = self.env['automatic.purchase.sales.match'].search([ + ('automatic_purchase_id', '=', self.id), + ('product_id', 'in', lines.mapped('product_id').ids), + ]) + match_map = {sm.product_id.id: sm for sm in sales_matches} + + for line in lines: + product = line.product_id + sales_match = match_map.get(product.id) + param_line = { + 'order_id': new_po.id, + 'product_id': product.id, + 'product_qty': line.qty_purchase, + 'qty_available_store': product.qty_available_bandengan, + 'suggest': product._get_po_suggest(line.qty_purchase), + 'product_uom_qty': line.qty_purchase, + 'price_unit': line.last_price, + 'ending_price': line.last_price, + 'taxes_id': [(6, 0, [line.taxes_id.id])] if line.taxes_id else False, + 'so_line_id': sales_match.sale_line_id.id if sales_match else None, + 'so_id': sales_match.sale_id.id if sales_match else None + } + 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.create_purchase_order_sales_match(new_po) - # Ambil baris sesuai halaman - lines = auto_purchase_line.search( - domain, - offset=i * PRODUCT_PER_PO, - limit=PRODUCT_PER_PO - ) - - for line in lines: - product = line.product_id - sales_match = self.env['automatic.purchase.sales.match'].search([ - ('automatic_purchase_id', '=', self.id), - ('product_id', '=', product.id), - ]) - param_line = { - 'order_id': new_po.id, - 'product_id': product.id, - 'product_qty': line.qty_purchase, - 'qty_available_store': product.qty_available_bandengan, - 'suggest': product._get_po_suggest(line.qty_purchase), - 'product_uom_qty': line.qty_purchase, - 'price_unit': line.last_price, - 'ending_price': line.last_price, - 'taxes_id': [line.taxes_id.id] if line.taxes_id else None, - 'so_line_id': sales_match[0].sale_line_id.id if sales_match else None, - 'so_id': sales_match[0].sale_id.id if sales_match else None - } - 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.create_purchase_order_sales_match(new_po) - - # Buat PO untuk special brand - if vendor_id == 23: - create_po_for_domain(special_brand_domain, special_payment_term=True) - - # Buat PO untuk regular domain - create_po_for_domain(regular_domain, "") def update_purchase_price_so_line(self, apo): diff --git a/indoteknik_custom/models/domain_apo.py b/indoteknik_custom/models/domain_apo.py new file mode 100644 index 00000000..585dd24c --- /dev/null +++ b/indoteknik_custom/models/domain_apo.py @@ -0,0 +1,12 @@ +from odoo import models, fields + + +class ApoDomainConfig(models.Model): + _name = 'apo.domain.config' + _description = 'Automatic Purchase Domain Config' + + name = fields.Char(string="Config Name", required=True) + vendor_id = fields.Many2one('res.partner', string="Vendor", required=True, domain=[('supplier_rank', '>', 0)]) + brand_ids = fields.Many2many('x_manufactures', string="Special Brands") + payment_term_id = fields.Many2one('account.payment.term', string="Payment Term") + is_special = fields.Boolean(string="Special Vendor?", default=False) diff --git a/indoteknik_custom/models/partial_delivery.py b/indoteknik_custom/models/partial_delivery.py new file mode 100644 index 00000000..c9d2ba5c --- /dev/null +++ b/indoteknik_custom/models/partial_delivery.py @@ -0,0 +1,189 @@ +from odoo import fields, models, api, _ +from odoo.exceptions import UserError, ValidationError +from datetime import datetime, timedelta, timezone, time +import logging, random, string, requests, math, json, re, qrcode, base64 + +_logger = logging.getLogger(__name__) + +class PartialDeliveryWizard(models.TransientModel): + _name = 'partial.delivery.wizard' + _description = 'Partial Delivery Wizard' + + sale_id = fields.Many2one('sale.order') + picking_ids = fields.Many2many('stock.picking') + picking_id = fields.Many2one( + 'stock.picking', + string='Delivery Order', + domain="[('id','in',picking_ids), ('state', 'not in', ('done', 'cancel')), ('name', 'like', 'BU/PICK/%')]" + ) + line_ids = fields.One2many('partial.delivery.wizard.line', 'wizard_id') + + # @api.model + # def default_get(self, fields_list): + # res = super().default_get(fields_list) + # picking_ids_ctx = self.env.context.get('default_picking_ids') + # lines = [] + # if picking_ids_ctx: + # if isinstance(picking_ids_ctx, list) and picking_ids_ctx and isinstance(picking_ids_ctx[0], tuple): + # picking_ids = picking_ids_ctx[0][2] + # else: + # picking_ids = picking_ids_ctx + + # pickings = self.env['stock.picking'].browse(picking_ids) + # moves = pickings.move_ids_without_package.filtered(lambda m: m.reserved_availability > 0) + + # for move in moves: + # lines.append((0, 0, { + # 'product_id': move.product_id.id, + # 'reserved_qty': move.reserved_availability, + # 'move_id': move.id, + # })) + # res['line_ids'] = lines + # return res + + @api.onchange('picking_id') + def _onchange_picking_id(self): + """Generate lines whenever picking_id is changed""" + lines = [] + if self.picking_id: + moves = self.picking_id.move_ids_without_package.filtered(lambda m: m.reserved_availability > 0) + for move in moves: + lines.append((0, 0, { + 'product_id': move.product_id.id, + 'reserved_qty': move.reserved_availability, + 'move_id': move.id, + })) + self.line_ids = lines + + + def action_confirm_partial_delivery(self): + self.ensure_one() + StockPicking = self.env['stock.picking'] + + picking = self.picking_id + if not picking: + raise UserError(_("Tidak ada picking yang dipilih.")) + + if picking.state != "assigned": + raise UserError(_("Picking harus dalam status Ready (assigned).")) + + + lines_by_qty = self.line_ids.filtered(lambda l: l.selected_qty > 0) + lines_by_selected = self.line_ids.filtered(lambda l: l.selected and not l.selected_qty) + selected_lines = lines_by_qty | lines_by_selected # gabung dua domain hasil filter + + if not selected_lines: + raise UserError(_("Tidak ada produk yang dipilih atau diisi jumlahnya.")) + + if selected_lines.selected_qty > selected_lines.reserved_qty: + raise UserError(_("Jumlah produk yang dipilih melebihi jumlah reserved.")) + + new_picking = StockPicking.create({ + 'origin': picking.origin, + 'partner_id': picking.partner_id.id, + 'picking_type_id': picking.picking_type_id.id, + 'location_id': picking.location_id.id, + 'location_dest_id': picking.location_dest_id.id, + 'company_id': picking.company_id.id, + 'state_reserve': 'partial', + }) + + for line in selected_lines: + move = line.move_id + move._do_unreserve() + + # kalau cuma selected tanpa isi qty, otomatis set selected_qty = reserved_qty + if line.selected and not line.selected_qty: + line.selected_qty = line.reserved_qty + + # MODE 1 → Prioritas kalau ada selected_qty + if line.selected_qty > 0: + if line.selected_qty > move.product_uom_qty: + raise UserError(_( + f"Qty kirim ({line.selected_qty}) untuk {move.product_id.display_name} melebihi qty move ({move.product_uom_qty})." + )) + + if line.selected_qty < move.product_uom_qty: + qty_to_keep = move.product_uom_qty - line.selected_qty + # split move + new_move = move.copy(default={ + 'product_uom_qty': line.selected_qty, + 'picking_id': new_picking.id, + 'partial': True, + }) + move.write({'product_uom_qty': qty_to_keep}) + else: + # full pindah + move.write({'picking_id': new_picking.id, 'partial': True}) + + + + # Confirm & assign DO baru + new_picking.action_confirm() + new_picking.action_assign() + + # Reassign DO lama biar sisa qty ke-update + picking.action_assign() + + # --- 🔢 Rename picking baru dengan format "/(Nomor urut)" --- + existing_partials = self.env['stock.picking'].search([ + ('origin', '=', picking.origin), + ('state_reserve', '=', 'partial'), + ('id', '!=', new_picking.id), + ], order='name asc') + + suffix_number = len(existing_partials) + if suffix_number == 0: + suffix_number = 1 + else: + suffix_number += 1 + + new_name = f"{picking.name}/{suffix_number}" + new_picking.name = new_name + + # --- 💬 Post message ke SO --- + if picking.origin: + sale_order = self.env['sale.order'].search([('name', '=', picking.origin)], limit=1) + if sale_order: + sale_order.message_post( + body=f"<b>Partial Delivery Created:</b> <a href=# data-oe-model='stock.picking' data-oe-id='{new_picking.id}'>{new_picking.name}</a> " + f"oleh {self.env.user.name}", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + # --- 📝 Log di DO baru --- + new_picking.message_post( + body=f"<b>Partial Picking created</b> dari {picking.name} oleh {self.env.user.name}", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + return { + "type": "ir.actions.act_window", + "res_model": "stock.picking", + "view_mode": "form", + "res_id": new_picking.id, + "target": "current", + "effect": { + "fadeout": "slow", + "message": f"🚚 Partial Delivery {new_picking.name} berhasil dibuat!", + "type": "rainbow_man", + }, + } + + + +class PartialDeliveryWizardLine(models.TransientModel): + _name = 'partial.delivery.wizard.line' + _description = 'Partial Delivery Wizard Line' + + wizard_id = fields.Many2one('partial.delivery.wizard') + product_id = fields.Many2one('product.product', string="Product") + reserved_qty = fields.Float(string="Reserved Qty") + selected_qty = fields.Float(string="Send Qty") + move_id = fields.Many2one('stock.move') + selected = fields.Boolean(string="Select") + sale_line_id = fields.Many2one('sale.order.line', string="SO Line", related='move_id.sale_line_id') + ordered_qty = fields.Float(related='sale_line_id.product_uom_qty', string="Ordered Qty") + diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 3aaae12d..57217894 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1813,10 +1813,10 @@ class SaleOrder(models.Model): # rec.commitment_date = rec.expected_ready_to_ship - @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + @api.onchange('expected_ready_to_ship') def _onchange_expected_ready_ship_date(self): self._validate_expected_ready_ship_date() - + def _set_etrts_date(self): for order in self: if order.state in ('done', 'cancel', 'sale'): @@ -2241,7 +2241,7 @@ class SaleOrder(models.Model): raise UserError("Payment Term pada Master Data Customer harus diisi") if not partner.active_limit and term_days > 0: raise UserError("Credit Limit pada Master Data Customer harus diisi") - if order.payment_term_id != partner.property_payment_term_id: + if order.payment_term_id != partner.property_payment_term_id and not order.partner_id.id == 29179: raise UserError("Payment Term berbeda pada Master Data Customer") if (partner.customer_type == 'pkp' or order.customer_type == 'pkp') and order.npwp != partner.npwp: raise UserError("NPWP berbeda pada Master Data Customer") @@ -2324,7 +2324,7 @@ class SaleOrder(models.Model): raise UserError("Payment Term pada Master Data Customer harus diisi") if not partner.active_limit and term_days > 0: raise UserError("Credit Limit pada Master Data Customer harus diisi") - if order.payment_term_id != partner.property_payment_term_id: + if order.payment_term_id != partner.property_payment_term_id and not order.partner_id.id == 29179: raise UserError("Payment Term berbeda pada Master Data Customer") if (partner.customer_type == 'pkp' or order.customer_type == 'pkp') and order.npwp != partner.npwp: raise UserError("NPWP berbeda pada Master Data Customer") @@ -3328,12 +3328,15 @@ class SaleOrder(models.Model): for order in self: partner = order.partner_id.parent_id or order.partner_id customer_payment_term = partner.property_payment_term_id - if vals['payment_term_id'] != customer_payment_term.id: + if vals['payment_term_id'] != customer_payment_term.id and not order.partner_id.id == 29179: raise UserError( f"Payment Term berbeda pada Master Data Customer. " f"Harap ganti ke '{customer_payment_term.name}' " f"sesuai dengan payment term yang terdaftar pada customer." ) + + if order.partner_id.id == 29179 and vals['payment_term_id'] not in [25,28]: + raise UserError(_("Pilih payment term 60 hari atau 30 hari.")) res = super(SaleOrder, self).write(vals) diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index d6505a86..b7db8775 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -19,6 +19,7 @@ class StockMove(models.Model): vendor_id = fields.Many2one('res.partner' ,string='Vendor') hold_outgoingg = fields.Boolean('Hold Outgoing', default=False) product_image = fields.Binary(related="product_id.image_128", string="Product Image", readonly=True) + partial = fields.Boolean('Partial?', default=False) # @api.model_create_multi # def create(self, vals_list): diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index a1adc90a..a6175b21 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -185,6 +185,9 @@ access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_match access_approval_payment_term,access.approval.payment.term,model_approval_payment_term,,1,1,1,1 access_purchase_order_update_date_wizard,access.purchase.order.update.date.wizard,model_purchase_order_update_date_wizard,,1,1,1,1 access_change_date_planned_wizard,access.change.date.planned.wizard,model_change_date_planned_wizard,,1,1,1,1 +access_partial_delivery_wizard,access.partial.delivery.wizard,model_partial_delivery_wizard,,1,1,1,1 +access_partial_delivery_wizard_line,access.partial.delivery.wizard.line,model_partial_delivery_wizard_line,,1,1,1,1 +access_apo_domain_config,access.apo.domain.config,model_apo_domain_config,base.group_user,1,1,1,1 access_refund_sale_order,access.refund.sale.order,model_refund_sale_order,base.group_user,1,1,1,1 access_refund_sale_order_line,access.refund.sale.order.line,model_refund_sale_order_line,base.group_user,1,1,1,1 diff --git a/indoteknik_custom/views/domain_apo.xml b/indoteknik_custom/views/domain_apo.xml new file mode 100644 index 00000000..1dae473d --- /dev/null +++ b/indoteknik_custom/views/domain_apo.xml @@ -0,0 +1,46 @@ +<odoo> + <record id="view_apo_domain_config_tree" model="ir.ui.view"> + <field name="name">apo.domain.config.tree</field> + <field name="model">apo.domain.config</field> + <field name="arch" type="xml"> + <tree> + <field name="name"/> + <field name="vendor_id"/> + <field name="brand_ids"/> + <field name="is_special"/> + <field name="payment_term_id"/> + </tree> + </field> + </record> + + <record id="view_apo_domain_config_form" model="ir.ui.view"> + <field name="name">apo.domain.config.form</field> + <field name="model">apo.domain.config</field> + <field name="arch" type="xml"> + <form> + <sheet> + <group> + <field name="name"/> + <field name="vendor_id"/> + <field name="brand_ids"/> + <field name="is_special"/> + <field name="payment_term_id"/> + </group> + </sheet> + </form> + </field> + </record> + + <record id="domain_apo_action" model="ir.actions.act_window"> + <field name="name">Domain APO</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">apo.domain.config</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem id="menu_automatic_purchase" + name="Domain APO" + action="domain_apo_action" + parent="menu_monitoring_in_purchase" + sequence="200"/> +</odoo> diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 8d56bbbd..eeb51eaa 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -7,6 +7,12 @@ <field name="inherit_id" ref="sale.view_order_form"/> <field name="arch" type="xml"> <button id="action_confirm" position="after"> + <button name="action_open_partial_delivery_wizard" + string="Partial Delivery" + type="object" + class="oe_highlight" + attrs="{'invisible': [('state','!=','sale')]}"/> + <button name="calculate_line_no" string="Create No" type="object" @@ -365,9 +371,14 @@ </field> <field name="payment_term_id" position="attributes"> <attribute name="attrs"> - {'readonly': ['|', ('approval_status', 'in', ['pengajuan1', 'pengajuan2', 'approved']), - ('state', 'not in', - ['cancel', 'draft'])]} + { + 'readonly': [ + '|', '|', + ('approval_status', 'in', ['pengajuan1', 'pengajuan2', 'approved']), + ('state', 'not in', ['cancel', 'draft']), + ('partner_id', 'not in', [29179]) + ] + } </attribute> </field> @@ -502,6 +513,37 @@ </field> </record> + <record id="view_partial_delivery_wizard_form" model="ir.ui.view"> + <field name="name">partial.delivery.wizard.form</field> + <field name="model">partial.delivery.wizard</field> + <field name="arch" type="xml"> + <form string="Partial Delivery"> + <sheet> + <group> + <field name="sale_id" invisible="1"/> + <field name="picking_ids" invisible="1"/> + <field name="picking_id" options="{'no_create': True, 'no_create_edit': True}" /> + <field name="line_ids" options="{'no_create': True, 'no_create_edit': True}"> + <tree editable="bottom"> + <field name="selected"/> + <field name="product_id" readonly="1" force_save="1"/> + <field name="move_id" readonly="1" force_save="1"/> + <field name="ordered_qty"/> + <field name="reserved_qty" readonly="1" force_save="1"/> + <field name="selected_qty" invisible="0"/> + </tree> + </field> + </group> + </sheet> + <footer> + <button string="Confirm" type="object" name="action_confirm_partial_delivery" class="btn-primary"/> + <button string="Cancel" special="cancel"/> + </footer> + </form> + </field> + </record> + + <record id="sale_order_multi_update_ir_actions_server" model="ir.actions.server"> <field name="name">Mark As Cancel</field> <field name="model_id" ref="sale.model_sale_order"/> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index d943f27a..44ab6355 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -207,6 +207,7 @@ <field name="product_uom" position="after"> <field name="sale_id" attrs="{'readonly': 1}" optional="hide"/> <field name="print_barcode" optional="hide"/> + <field name="partial" widget="boolean_toggle" optional="hide"/> <field name="qr_code_variant" widget="image" optional="hide"/> <field name="barcode" optional="hide"/> </field> |
