summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-07-08 08:58:46 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-07-08 08:58:46 +0700
commitb858358ffbdd14c9b56ac96f035bddccae4d872d (patch)
tree0058aeea123b8e2c29ab704f98806b227838dbbe
parent8f07d24c8362cb6a4d5ded8f94b75c5057a5b025 (diff)
skema bills and requisition
-rwxr-xr-xfixco_custom/models/__init__.py1
-rw-r--r--fixco_custom/models/account_move.py60
-rw-r--r--fixco_custom/models/account_move_line.py19
-rw-r--r--fixco_custom/models/purchase_order.py8
-rw-r--r--fixco_custom/models/requisition.py55
-rw-r--r--fixco_custom/views/account_move.xml52
-rw-r--r--fixco_custom/views/purchase_order.xml2
-rw-r--r--fixco_custom/views/requisition.xml2
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>