diff options
Diffstat (limited to 'fixco_custom')
| -rwxr-xr-x | fixco_custom/__manifest__.py | 6 | ||||
| -rwxr-xr-x | fixco_custom/models/__init__.py | 4 | ||||
| -rw-r--r-- | fixco_custom/models/automatic_purchase.py | 93 | ||||
| -rw-r--r-- | fixco_custom/models/brands.py | 16 | ||||
| -rw-r--r-- | fixco_custom/models/manage_stock.py | 2 | ||||
| -rwxr-xr-x | fixco_custom/models/product_product.py | 2 | ||||
| -rw-r--r-- | fixco_custom/models/product_public_category.py | 23 | ||||
| -rw-r--r-- | fixco_custom/models/purchasing_job.py | 13 | ||||
| -rwxr-xr-x | fixco_custom/security/ir.model.access.csv | 3 | ||||
| -rw-r--r-- | fixco_custom/views/automatic_purchase.xml | 2 | ||||
| -rw-r--r-- | fixco_custom/views/brands_views.xml | 59 | ||||
| -rwxr-xr-x | fixco_custom/views/product_product.xml | 2 | ||||
| -rw-r--r-- | fixco_custom/views/product_public_category.xml | 55 | ||||
| -rw-r--r-- | fixco_custom/views/purchasing_job.xml | 1 |
14 files changed, 241 insertions, 40 deletions
diff --git a/fixco_custom/__manifest__.py b/fixco_custom/__manifest__.py index 12e15ca..0bcdfaa 100755 --- a/fixco_custom/__manifest__.py +++ b/fixco_custom/__manifest__.py @@ -5,10 +5,10 @@ 'sequence': 1, 'summary': 'Fixco Custom', 'description': '', - 'author': 'Stephan Christianus', + 'author': 'Stephan Christianus, Azka Nathan', 'website': '', 'images': ['assets/favicon.ico'], - 'depends': ['base', 'sale'], + 'depends': ['base', 'coupon', 'delivery', 'sale', 'sale_management'], 'data': [ 'security/ir.model.access.csv', 'views/res_partner.xml', @@ -37,6 +37,8 @@ 'views/automatic_purchase.xml', 'views/purchasing_job.xml', 'views/upload_bills.xml', + 'views/brands_views.xml', + 'views/product_public_category.xml', ], 'demo': [], 'css': [], diff --git a/fixco_custom/models/__init__.py b/fixco_custom/models/__init__.py index f345625..e80acd8 100755 --- a/fixco_custom/models/__init__.py +++ b/fixco_custom/models/__init__.py @@ -23,4 +23,6 @@ from . import manage_stock from . import automatic_purchase from . import purchasing_job from . import stock_move -from . import upload_bills
\ No newline at end of file +from . import upload_bills +from . import brands +from . import product_public_category
\ No newline at end of file diff --git a/fixco_custom/models/automatic_purchase.py b/fixco_custom/models/automatic_purchase.py index 1ef12ca..98a4136 100644 --- a/fixco_custom/models/automatic_purchase.py +++ b/fixco_custom/models/automatic_purchase.py @@ -77,6 +77,8 @@ class AutomaticPurchase(models.Model): 'partner_id': vendor.id if vendor else False, 'taxes_id': vendor.tax_id.id if vendor else False, 'price': price, + 'product_public_category_id': product.product_public_category_id.id, + 'brand_id': product.brand_id.id, }) self.env['automatic.purchase.line'].create(lines) @@ -111,42 +113,72 @@ class AutomaticPurchase(models.Model): self.ensure_one() if not self.automatic_purchase_lines: raise UserError(_("No purchase lines to process!")) - + if self.is_po: raise UserError(_("Purchase order already created!")) - + vendor_lines = {} + for line in self.automatic_purchase_lines: - if line.partner_id.id not in vendor_lines: - vendor_lines[line.partner_id.id] = [] - vendor_lines[line.partner_id.id].append(line) - + partner_id = line.partner_id.id + brand_id = line.brand_id.id if line.brand_id else 0 + category_id = line.product_public_category_id.id if line.product_public_category_id else 0 + + if partner_id != 270: + key = (partner_id,) + elif brand_id == 3: + key = (partner_id, brand_id, category_id) + else: + key = (partner_id, brand_id) + + vendor_lines.setdefault(key, []).append(line) + created_orders = self.env['purchase.order'] - - for vendor_id, lines in vendor_lines.items(): - vendor = self.env['res.partner'].browse(vendor_id) - - chunk_size = 10 + + for key, lines in vendor_lines.items(): + partner_id = key[0] + brand_id = key[1] if len(key) > 1 else None + category_id = key[2] if len(key) > 2 else None + + vendor = self.env['res.partner'].browse(partner_id) + + chunk_size = 20 line_chunks = [lines[i:i + chunk_size] for i in range(0, len(lines), chunk_size)] - + for index, chunk in enumerate(line_chunks): - order = self._create_purchase_order(vendor, index + 1, len(line_chunks)) + order = self._create_purchase_order( + vendor, + index + 1, + len(line_chunks), + brand_id=brand_id, + category_id=category_id + ) created_orders += order - + for line in chunk: self._create_purchase_order_line(order, line) - + self.purchase_order_ids = [(6, 0, created_orders.ids)] self.is_po = True - + return self.action_view_purchase_orders() + + - def _create_purchase_order(self, vendor, sequence, total_chunks): - """Membuat purchase order untuk vendor""" + def _create_purchase_order(self, vendor, sequence, total_chunks, brand_id=None, category_id=None): order_name = f"{self.number} - {vendor.name}" + + if brand_id: + brand = self.env['brands'].browse(brand_id) + order_name += f" - {brand.name}" + + if category_id: + cat = self.env['product.public.category'].browse(category_id) + order_name += f" - {cat.name}" + if total_chunks > 1: order_name += f" ({sequence}/{total_chunks})" - + return self.env['purchase.order'].create({ 'partner_id': vendor.id, 'origin': self.number, @@ -155,10 +187,12 @@ class AutomaticPurchase(models.Model): 'currency_id': vendor.property_purchase_currency_id.id or self.env.company.currency_id.id, 'automatic_purchase_id': self.id, 'source': self.apo_type, + 'name': order_name, }) + + def _create_purchase_order_line(self, order, line): - """Membuat purchase order line dari automatic purchase line""" product = line.product_id return self.env['purchase.order.line'].create({ @@ -175,24 +209,20 @@ class AutomaticPurchase(models.Model): def generate_automatic_lines(self): self.ensure_one() - # Hapus lines yang sudah ada self.automatic_purchase_lines.unlink() manage_stocks = self.env['manage.stock'].search([]) - location_id = 55 # Lokasi gudang ID 55 + location_id = 55 lines_to_create = [] for stock in manage_stocks: - # Cari semua stock.quant untuk produk ini di lokasi 55 quant_records = self.env['stock.quant'].search([ ('product_id', '=', stock.product_id.id), ('location_id', '=', location_id) ]) - # Hitung total qty_available (quantity - reserved_quantity) untuk lokasi tersebut total_available = quant_records.quantity or 0.0 - # Log nilai untuk debugging _logger.info( "Product %s: Available=%.4f, Min=%.4f, Buffer=%.4f", stock.product_id.display_name, @@ -201,14 +231,11 @@ class AutomaticPurchase(models.Model): stock.buffer_stock ) - # Gunakan float_compare untuk perbandingan yang akurat comparison = float_compare(total_available, stock.min_stock, precision_rounding=0.0001) - if comparison <= 0: # Termasuk saat sama dengan min_stock - # Hitung qty yang perlu dipesan + if comparison <= 0: qty_purchase = stock.buffer_stock - total_available - # Pastikan qty_purchase positif if float_compare(qty_purchase, 0.0, precision_rounding=0.0001) <= 0: _logger.warning( "Negative purchase quantity for %s: Available=%.4f, Buffer=%.4f, Purchase=%.4f", @@ -219,7 +246,6 @@ class AutomaticPurchase(models.Model): ) continue - # Dapatkan harga dari purchase.pricelist pricelist = self.env['purchase.pricelist'].search([ ('product_id', '=', stock.product_id.id), ('vendor_id', '=', stock.vendor_id.id) @@ -228,7 +254,6 @@ class AutomaticPurchase(models.Model): price = pricelist.price if pricelist else 0.0 subtotal = qty_purchase * price - # Log penambahan produk _logger.info( "Adding product %s: Available=%.4f, Min=%.4f, Purchase=%.4f", stock.product_id.display_name, @@ -237,7 +262,6 @@ class AutomaticPurchase(models.Model): qty_purchase ) - # Kumpulkan data untuk pembuatan lines lines_to_create.append({ 'automatic_purchase_id': self.id, 'product_id': stock.product_id.id, @@ -247,6 +271,8 @@ class AutomaticPurchase(models.Model): 'partner_id': stock.vendor_id.id, 'taxes_id': stock.vendor_id.tax_id.id, 'price': price, + 'product_public_category_id': stock.product_id.product_public_category_id.id, + 'brand_id': stock.product_id.brand_id.id, }) else: _logger.info( @@ -256,7 +282,6 @@ class AutomaticPurchase(models.Model): stock.min_stock ) - # Buat lines sekaligus untuk efisiensi if lines_to_create: self.env['automatic.purchase.line'].create(lines_to_create) _logger.info("Created %d purchase lines", len(lines_to_create)) @@ -291,6 +316,8 @@ class AutomaticPurchaseLine(models.Model): subtotal = fields.Float(string='Subtotal', compute='compute_subtotal') is_po = fields.Boolean(String='Is PO') taxes_id = fields.Many2one('account.tax', string='Taxes') + brand_id = fields.Many2one('brands', string='Brand') + product_public_category_id = fields.Many2one('product.public.category', string='Public Category') def compute_subtotal(self): for line in self: diff --git a/fixco_custom/models/brands.py b/fixco_custom/models/brands.py new file mode 100644 index 0000000..378adeb --- /dev/null +++ b/fixco_custom/models/brands.py @@ -0,0 +1,16 @@ +from odoo import models, fields # type: ignore + +class Brands(models.Model): + _name = 'brands' + _description = 'Brands' + + name = fields.Char(string='Brand Name', required=True) + product_ids = fields.One2many('product.product', 'brand_id', string='Products') + category_ids = fields.Many2many( + 'product.public.category', + # 'brands_category_rel', + # 'brand_id', + # 'category_id', + string='Public Categories' +) +
\ No newline at end of file diff --git a/fixco_custom/models/manage_stock.py b/fixco_custom/models/manage_stock.py index 29d482f..80aa505 100644 --- a/fixco_custom/models/manage_stock.py +++ b/fixco_custom/models/manage_stock.py @@ -78,6 +78,8 @@ class ManageStock(models.Model): 'partner_id': stock.vendor_id.id, 'taxes_id': stock.vendor_id.tax_id.id if stock.vendor_id.tax_id else False, 'price': price, + 'product_public_category_id': stock.product_id.product_public_category_id.id, + 'brand_id': stock.product_id.brand_id.id, }) self.env['automatic.purchase.line'].create(lines_to_create) diff --git a/fixco_custom/models/product_product.py b/fixco_custom/models/product_product.py index 5117104..72e7ecd 100755 --- a/fixco_custom/models/product_product.py +++ b/fixco_custom/models/product_product.py @@ -18,6 +18,8 @@ class ProductProduct(models.Model): barcode_box = fields.Char("Barcode Box") qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') qty_multiple = fields.Float('Multiple') + brand_id = fields.Many2one('brands', string='Brand') + product_public_category_id = fields.Many2one('product.public.category', string='Public Categories') def check_multiple_qty(self, other_qty): if self.qty_multiple > 0 and other_qty > 0: diff --git a/fixco_custom/models/product_public_category.py b/fixco_custom/models/product_public_category.py new file mode 100644 index 0000000..b706bc1 --- /dev/null +++ b/fixco_custom/models/product_public_category.py @@ -0,0 +1,23 @@ +from odoo import fields, models, api + + +class ProductPublicCategory(models.Model): + _inherit = "product.public.category" + + brand_id = fields.Many2one('brands', string='Brand') + product_ids = fields.One2many('product.product', 'product_public_category_id', string='Products') + + # @api.model + # def _onchange_child_frontend_id2(self, parent): + # domain = {} + # if parent: + # parent_records = self.browse(parent) + # parent_names = parent_records.mapped('name') + # domain['child_frontend_id2'] = [ + # ('parent_frontend_id', 'in', parent), + # ('parent_frontend_id.name', 'ilike', parent_names) + # ] + # else: + # domain['child_frontend_id2'] = [] + # return {'domain': domain} + diff --git a/fixco_custom/models/purchasing_job.py b/fixco_custom/models/purchasing_job.py index 1af47b7..2c7138a 100644 --- a/fixco_custom/models/purchasing_job.py +++ b/fixco_custom/models/purchasing_job.py @@ -17,6 +17,7 @@ class PurchasingJob(models.Model): action = fields.Char(string='Action') product_id = fields.Many2one('product.product', string='Product') vendor_id = fields.Many2one('res.partner', string='Vendor') + brand_id = fields.Many2one('brands', string='Brand') def create_automatic_purchase(self): if not self: @@ -54,6 +55,8 @@ class PurchasingJob(models.Model): 'partner_id': stock.vendor_id.id, 'taxes_id': stock.vendor_id.tax_id.id if stock.vendor_id.tax_id else False, 'price': price, + 'brand_id': stock.brand_id.id, + 'product_public_category_id': stock.product_id.product_public_category_id.id, }) self.env['automatic.purchase.line'].create(lines_to_create) @@ -83,7 +86,8 @@ class PurchasingJob(models.Model): ELSE 'cukup' END AS action, a.product_id, - pp2.vendor_id + pp2.vendor_id, + b.id AS brand_id FROM ( SELECT COALESCE(pp.default_code, pt.default_code) AS item_code, @@ -91,7 +95,8 @@ class PurchasingJob(models.Model): get_qty_onhand(pp.id::numeric) AS onhand, get_qty_incoming(pp.id::numeric) AS incoming, get_qty_outgoing(pp.id::numeric) AS outgoing, - pp.id AS product_id + pp.id AS product_id, + pp.brand_id FROM stock_move sm JOIN stock_picking sp ON sp.id = sm.picking_id JOIN product_product pp ON pp.id = sm.product_id @@ -99,8 +104,9 @@ class PurchasingJob(models.Model): WHERE sp.state IN ('draft', 'waiting', 'confirmed', 'assigned') AND sp.name LIKE '%%OUT%%' AND sm.location_id = 55 - GROUP BY pp.id, pp.default_code, pt.default_code, pt.name + GROUP BY pp.id, pp.default_code, pt.default_code, pt.name, pp.brand_id ) a + LEFT JOIN brands b ON b.id = a.brand_id LEFT JOIN LATERAL ( SELECT vendor_id FROM purchase_pricelist @@ -116,3 +122,4 @@ class PurchasingJob(models.Model): + diff --git a/fixco_custom/security/ir.model.access.csv b/fixco_custom/security/ir.model.access.csv index a3f7ac4..4b22abd 100755 --- a/fixco_custom/security/ir.model.access.csv +++ b/fixco_custom/security/ir.model.access.csv @@ -28,4 +28,5 @@ access_automatic_purchase_line,access.automatic.purchase.line,model_automatic_pu access_automatic_purchase,access.automatic.purchase,model_automatic_purchase,,1,1,1,1 access_purchasing_job,access.purchasing.job,model_purchasing_job,,1,1,1,1 access_upload_bills,access.upload.bills,model_upload_bills,,1,1,1,1 -access_upload_bills_line,access.upload.bills.line,model_upload_bills_line,,1,1,1,1
\ No newline at end of file +access_upload_bills_line,access.upload.bills.line,model_upload_bills_line,,1,1,1,1 +access_brands,access.brands,model_brands,,1,1,1,1
\ No newline at end of file diff --git a/fixco_custom/views/automatic_purchase.xml b/fixco_custom/views/automatic_purchase.xml index f985a54..9b0a45b 100644 --- a/fixco_custom/views/automatic_purchase.xml +++ b/fixco_custom/views/automatic_purchase.xml @@ -20,6 +20,8 @@ <field name="arch" type="xml"> <tree editable="bottom"> <field name="product_id"/> + <field name="brand_id"/> + <field name="product_public_category_id"/> <field name="partner_id" required="1"/> <field name="taxes_id" domain="[('type_tax_use','=','purchase')]"/> <field name="qty_purchase"/> diff --git a/fixco_custom/views/brands_views.xml b/fixco_custom/views/brands_views.xml new file mode 100644 index 0000000..09fc75a --- /dev/null +++ b/fixco_custom/views/brands_views.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <record id="view_brands_form" model="ir.ui.view"> + <field name="name">brands.form</field> + <field name="model">brands</field> + <field name="arch" type="xml"> + <form string="Brand"> + <sheet> + <group> + <field name="name"/> + </group> + <notebook> + <page string="Products"> + <field name="product_ids"> + <tree editable="bottom"> + <field name="name"/> + <field name="default_code"/> + <field name="list_price"/> + </tree> + </field> + </page> + <page string="Categories"> + <field name="category_ids"> + <tree> + <field name="name"/> + </tree> + </field> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + + <record id="view_brands_tree" model="ir.ui.view"> + <field name="name">brands.tree</field> + <field name="model">brands</field> + <field name="arch" type="xml"> + <tree> + <field name="name"/> + </tree> + </field> + </record> + + <record id="brands_action" model="ir.actions.act_window"> + <field name="name">Brands</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">brands</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem + id="menu_brands" + name="Brands" + parent="sale_management.menu_product_attribute_action" + sequence="4" + action="brands_action" + /> +</odoo>
\ No newline at end of file diff --git a/fixco_custom/views/product_product.xml b/fixco_custom/views/product_product.xml index 6c468ba..08952b6 100755 --- a/fixco_custom/views/product_product.xml +++ b/fixco_custom/views/product_product.xml @@ -7,10 +7,12 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> <field name="categ_id" position="after"> + <field name="brand_id" /> <field name="qty_multiple" /> <field name="barcode_box" /> <field name="qty_pcs_box" /> <field name="qr_code_variant" widget="image" readonly="True"/> + <field name="product_public_category_id" attrs="{'required': [('brand_id', '=', 3)]}"/> </field> <field name="uom_po_id" position="after"> <field name="qty_multiple" /> diff --git a/fixco_custom/views/product_public_category.xml b/fixco_custom/views/product_public_category.xml new file mode 100644 index 0000000..090321f --- /dev/null +++ b/fixco_custom/views/product_public_category.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <record id="view_product_public_category_form_custom" model="ir.ui.view"> + <field name="name">product.public.category.form.custom</field> + <field name="model">product.public.category</field> + <field name="arch" type="xml"> + <form string="Public Category"> + <sheet> + <group> + <field name="name"/> + <field name="parent_id"/> + <field name="brand_id"/> + </group> + <notebook> + <page string="Products"> + <field name="product_ids"> + <tree editable="bottom"> + <field name="name"/> + <field name="default_code"/> + <field name="list_price"/> + </tree> + </field> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + + <record id="view_product_public_category_tree_custom" model="ir.ui.view"> + <field name="name">product.public.category.tree.custom</field> + <field name="model">product.public.category</field> + <field name="arch" type="xml"> + <tree> + <field name="name"/> + <field name="parent_id"/> + <field name="brand_id"/> + </tree> + </field> + </record> + + <record id="action_product_public_category_custom" model="ir.actions.act_window"> + <field name="name">Public Categories</field> + <field name="res_model">product.public.category</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem + id="menu_product_public_category_custom" + name="Public Categories" + parent="sale.product_menu_catalog" + action="action_product_public_category_custom" + sequence="20" + /> +</odoo>
\ No newline at end of file diff --git a/fixco_custom/views/purchasing_job.xml b/fixco_custom/views/purchasing_job.xml index 7d16682..7a15160 100644 --- a/fixco_custom/views/purchasing_job.xml +++ b/fixco_custom/views/purchasing_job.xml @@ -8,6 +8,7 @@ <tree string="Procurement Monitoring by Product" create="false" delete="false"> <field name="item_code"/> <field name="product"/> + <field name="brand_id"/> <field name="vendor_id"/> <field name="onhand"/> <field name="incoming"/> |
