diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2025-07-08 08:58:46 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2025-07-08 08:58:46 +0700 |
| commit | b858358ffbdd14c9b56ac96f035bddccae4d872d (patch) | |
| tree | 0058aeea123b8e2c29ab704f98806b227838dbbe | |
| parent | 8f07d24c8362cb6a4d5ded8f94b75c5057a5b025 (diff) | |
skema bills and requisition
| -rwxr-xr-x | fixco_custom/models/__init__.py | 1 | ||||
| -rw-r--r-- | fixco_custom/models/account_move.py | 60 | ||||
| -rw-r--r-- | fixco_custom/models/account_move_line.py | 19 | ||||
| -rw-r--r-- | fixco_custom/models/purchase_order.py | 8 | ||||
| -rw-r--r-- | fixco_custom/models/requisition.py | 55 | ||||
| -rw-r--r-- | fixco_custom/views/account_move.xml | 52 | ||||
| -rw-r--r-- | fixco_custom/views/purchase_order.xml | 2 | ||||
| -rw-r--r-- | fixco_custom/views/requisition.xml | 2 |
8 files changed, 168 insertions, 31 deletions
diff --git a/fixco_custom/models/__init__.py b/fixco_custom/models/__init__.py index 4bc9e44..c12e9a7 100755 --- a/fixco_custom/models/__init__.py +++ b/fixco_custom/models/__init__.py @@ -19,3 +19,4 @@ from . import upload_ginee from . import purchase_order_line from . import purchase_order from . import requisition +from . import account_move_line diff --git a/fixco_custom/models/account_move.py b/fixco_custom/models/account_move.py index 0cdc22d..0a417cd 100644 --- a/fixco_custom/models/account_move.py +++ b/fixco_custom/models/account_move.py @@ -23,6 +23,14 @@ class AccountMove(models.Model): ('difaktur', 'Faktur Pajak')], string='Transaction Type' ) + purchase_vendor_bill_ids = fields.Many2many( + 'purchase.bill.union', + string='Auto-complete', + store=False, + readonly=True, + states={'draft': [('readonly', False)]}, + help="Auto-complete from multiple past bills / purchase orders.", + ) def action_post(self): @@ -30,4 +38,54 @@ class AccountMove(models.Model): for entry in self: entry.invoice_date = entry.picking_id.date_done - return res
\ No newline at end of file + return res + + @api.onchange('purchase_vendor_bill_ids', 'purchase_id') + def _onchange_purchase_auto_complete(self): + """ Load from either multiple old purchase orders or vendor bills. """ + + vendor_bills = self.purchase_vendor_bill_ids.mapped('vendor_bill_id') + purchase_orders = self.purchase_vendor_bill_ids.mapped('purchase_order_id') + + for bill in vendor_bills: + self.invoice_vendor_bill_id = bill + self._onchange_invoice_vendor_bill() + + for po in purchase_orders: + self.purchase_id = po + + invoice_vals = po.with_company(po.company_id)._prepare_invoice() + invoice_vals['currency_id'] = self.line_ids and self.currency_id or invoice_vals.get('currency_id') + invoice_vals.pop('ref', None) + self.update(invoice_vals) + + po_lines = po.order_line - self.line_ids.mapped('purchase_line_id') + new_lines = self.env['account.move.line'] + sequence = max(self.line_ids.mapped('sequence')) + 1 if self.line_ids else 10 + + for line in po_lines.filtered(lambda l: not l.display_type): + line_vals = line._prepare_account_move_line(self) + line_vals.update({'sequence': sequence}) + new_line = new_lines.new(line_vals) + sequence += 1 + new_line.account_id = new_line._get_computed_account() + new_line._onchange_price_subtotal() + new_lines += new_line + + new_lines._onchange_mark_recompute_taxes() + + # Compute invoice_origin + origins = set(self.line_ids.mapped('purchase_line_id.order_id.name')) + self.invoice_origin = ', '.join(origins) + + # Compute ref + refs = self._get_invoice_reference() + self.ref = ', '.join(refs) + + # Compute payment_reference + if len(refs) == 1: + self.payment_reference = refs[0] + + self.purchase_id = False + self.purchase_vendor_bill_ids = [(5, 0, 0)] # clear after use + self._onchange_currency() diff --git a/fixco_custom/models/account_move_line.py b/fixco_custom/models/account_move_line.py new file mode 100644 index 0000000..cf4d5d7 --- /dev/null +++ b/fixco_custom/models/account_move_line.py @@ -0,0 +1,19 @@ +from odoo import models, api, fields, _ +from odoo.exceptions import AccessError, UserError, ValidationError + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + qty_outstanding = fields.Float(string='Qty Outstanding', compute='_compute_qty_outstanding') + + def _compute_qty_outstanding(self): + for line in self: + qty_received = line.purchase_line_id.qty_received + qty_billed = line.purchase_line_id.qty_invoiced + line.qty_outstanding = qty_received - qty_billed + + @api.onchange('quantity') + def _onchange_quantity(self): + for line in self: + if line.quantity > line.qty_outstanding: + raise UserError(_("Quantity Tidak Boleh Melebihi Qty Outstanding")) diff --git a/fixco_custom/models/purchase_order.py b/fixco_custom/models/purchase_order.py index a623ae4..06f4ef1 100644 --- a/fixco_custom/models/purchase_order.py +++ b/fixco_custom/models/purchase_order.py @@ -17,6 +17,7 @@ _logger = logging.getLogger(__name__) class PurchaseOrder(models.Model): _inherit = 'purchase.order' + sale_order_id = fields.Many2one('sale.order', string='Sales Order') amount_discount = fields.Monetary( string='Total Discount', compute='_compute_amount_discount', @@ -28,6 +29,13 @@ class PurchaseOrder(models.Model): default=0.0, tracking=True ) + + source = fields.Selection([ + ('requisition', 'Requisition'), + ('reordering', 'Reordering'), + ('purchasing_job', 'Purchasing Job'), + ('manual', 'Manual') + ], string='Source', default='manual') @api.depends('order_line.price_total', 'biaya_lain_lain') def _amount_all(self): diff --git a/fixco_custom/models/requisition.py b/fixco_custom/models/requisition.py index 1734abd..b0800ba 100644 --- a/fixco_custom/models/requisition.py +++ b/fixco_custom/models/requisition.py @@ -51,24 +51,31 @@ class Requisition(models.Model): sales_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False) merchandise_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False) + def get_price(self, product): + purchase_pricelist = self.env['purchase.pricelist'].search([ + ('product_id', '=', product) + ], limit=1) + if purchase_pricelist: + return purchase_pricelist.price, purchase_pricelist.vendor_id.id + 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') + # 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: + price, vendor = self.get_price(order_line.product_id.id) 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 + 'partner_id': vendor, + 'price_unit': price, + 'taxes_id': 14, + 'subtotal': price * order_line.product_uom_qty } self.env['requisition.line'].create([param]) @@ -90,8 +97,8 @@ class Requisition(models.Model): self.merchandise_approve = True def create_po_from_requisition(self): - if not self.sales_approve and not self.merchandise_approve: - raise UserError('Harus Di Approve oleh Darren atau Rafly') + # if not self.sales_approve and not self.merchandise_approve: + # raise UserError('Harus Di Approve oleh Darren atau Rafly') if not self.requisition_lines: raise UserError('Tidak ada Lines, belum bisa create PO') if self.is_po: @@ -133,7 +140,8 @@ class Requisition(models.Model): 'picking_type_id': 28, # indoteknik bandengan receipts 'date_order': current_time, 'sale_order_id': self.sale_order_id.id, - 'note_description': 'from Purchase Requisition' + # 'source': 'requisition', + # 'note_description': 'from Purchase Requisition' } domain = [ @@ -148,7 +156,7 @@ class Requisition(models.Model): # 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) + new_po.source = 'requisition' po_ids.append(new_po.id) lines = requisition_line.search( domain, @@ -169,8 +177,8 @@ class Requisition(models.Model): '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 + # 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, @@ -275,7 +283,6 @@ class RequisitionLine(models.Model): _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') @@ -318,14 +325,12 @@ class RequisitionLine(models.Model): @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) + ], limit=1) - price, taxes = line._get_valid_purchase_price(purchase_pricelist) - line.price_unit = price - line.taxes_id = taxes + # price, taxes = line._get_valid_purchase_price(purchase_pricelist) + line.price_unit = purchase_pricelist.price line.partner_id = purchase_pricelist.vendor_id.id @api.model @@ -354,10 +359,10 @@ class RequisitionLine(models.Model): message = f"Produk telah di-{action} : <br/>" if action == 'Tambah': # message += f"<br/> Product: {self.product_id.name}" - message += f"Product: {self.product_id.name} <br/> Vendor: {self.partner_id.name} <br/> Qty: {self.qty_purchase} <br/> Price: {self.price_unit} <br/> Tax: {self.tax_id.name} <br/> Subtotal: {self.subtotal} <br/> Brand: {self.brand_id.x_name}" + message += f"Product: {self.product_id.name} <br/> Vendor: {self.partner_id.name} <br/> Qty: {self.qty_purchase} <br/> Price: {self.price_unit} <br/> Tax: {self.tax_id.name} <br/> Subtotal: {self.subtotal} <br/>" elif action == 'Hapus': # message += f"<br/> Deleted Product: {self.product_id.name}" - message += f"<br/> Deleted Product: {self.product_id.name} <br/> Vendor: {self.partner_id.name} Qty: {self.qty_purchase} <br/> Price: {self.price_unit} <br/> Tax: {self.tax_id.name} <br/> Subtotal: {self.subtotal} <br/> Brand: {self.brand_id.x_name}" + message += f"<br/> Deleted Product: {self.product_id.name} <br/> Vendor: {self.partner_id.name} Qty: {self.qty_purchase} <br/> Price: {self.price_unit} <br/> Tax: {self.tax_id.name} <br/> Subtotal: {self.subtotal} <br/>" else: # Updated for field, old_value in old_values.items(): new_value = self[field] diff --git a/fixco_custom/views/account_move.xml b/fixco_custom/views/account_move.xml index c77d046..1c55831 100644 --- a/fixco_custom/views/account_move.xml +++ b/fixco_custom/views/account_move.xml @@ -6,20 +6,66 @@ <field name="model">account.move</field> <field name="inherit_id" ref="account.view_move_form"/> <field name="arch" type="xml"> - <!-- <field name="ref" position="after"> - <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'entry')]}"/> - </field> --> + <field name="payment_reference" position="after"> <field name="invoice_marketplace" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> <field name="transaction_type" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> </field> + <field name="partner_id" position="after"> <field name="address" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> </field> + <field name="invoice_date" position="after"> <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> <field name="picking_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> </field> + + <!-- ✅ Add the new Many2many field after invoice_vendor_bill_id --> + <field name="invoice_vendor_bill_id" position="after"> + <field name="purchase_id" invisible="1"/> + + <label for="purchase_vendor_bill_ids" string="Auto-Complete" class="oe_edit_only" + attrs="{'invisible': ['|', ('state','!=','draft'), ('move_type', '!=', 'in_invoice')]}" /> + + <field name="purchase_vendor_bill_ids" nolabel="1" + attrs="{'invisible': ['|', ('state','!=','draft'), ('move_type', '!=', 'in_invoice')]}" + domain="partner_id and [('company_id', '=', company_id), ('partner_id.commercial_partner_id', '=', commercial_partner_id)] or [('company_id', '=', company_id)]" + placeholder="Select purchase orders or past bills" + context="{'show_total_amount': True}" + options="{'no_create': True, 'no_open': True}"/> + </field> + + <!-- Purchase line_id on invoice line --> + <xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='company_id']" position="after"> + <field name="purchase_line_id" invisible="1"/> + <field name="purchase_order_id" + attrs="{'column_invisible': [('parent.move_type', '!=', 'in_invoice')]}" + optional="hide"/> + </xpath> + + <xpath expr="//field[@name='line_ids']/tree/field[@name='company_id']" position="after"> + <field name="purchase_line_id" invisible="1"/> + </xpath> + + </field> + </record> + + <record id="account_move_inherit_purchase_inherit" model="ir.ui.view"> + <field name="name">Account Move</field> + <field name="model">account.move</field> + <field name="inherit_id" ref="purchase.view_move_form_inherit_purchase"/> + <field name="arch" type="xml"> + <field name="purchase_line_id" position="attributes"> + <attribute name="invisible">0</attribute> + <attribute name="optional">hide</attribute> + <attribute name="attrs"> + {'column_invisible': [('parent.move_type', '!=', 'in_invoice')]} + </attribute> + </field> + <field name="quantity" position="after"> + <field name="qty_outstanding" optional="hide"/> + </field> </field> </record> </data> diff --git a/fixco_custom/views/purchase_order.xml b/fixco_custom/views/purchase_order.xml index 1aa438c..de8c7bb 100644 --- a/fixco_custom/views/purchase_order.xml +++ b/fixco_custom/views/purchase_order.xml @@ -7,7 +7,9 @@ <field name="inherit_id" ref="purchase.purchase_order_form"/> <field name="arch" type="xml"> <field name="currency_id" position="after"> + <field name="sale_order_id" readonly="1"/> <field name="biaya_lain_lain"/> + <field name="source"/> </field> <field name="amount_untaxed" position="after"> <field name="amount_discount" class="oe_currency_line"/> diff --git a/fixco_custom/views/requisition.xml b/fixco_custom/views/requisition.xml index 957113a..3648401 100644 --- a/fixco_custom/views/requisition.xml +++ b/fixco_custom/views/requisition.xml @@ -19,7 +19,6 @@ <field name="model">requisition.line</field> <field name="arch" type="xml"> <tree> - <field name="brand_id"/> <field name="product_id"/> <field name="partner_id"/> <field name="qty_purchase"/> @@ -94,7 +93,6 @@ <field name="price_unit" required="1" /> <field name="taxes_id" readonly="1" /> <field name="subtotal" readonly="1" /> - <field name="brand_id" /> </tree> </field> </page> |
