summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models/automatic_purchase.py
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2024-03-26 13:50:27 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2024-03-26 13:50:27 +0700
commit935393a6a58b3df18f3361b36a6ea60647cd8be4 (patch)
treee4ec9d300aff98a38a8524c209a19f970aa24f23 /indoteknik_custom/models/automatic_purchase.py
parentdb1780524b1e153338ced116f786b4d712d66aca (diff)
parentb2377426bec8aa334277aac48b0b25f0dfac420f (diff)
Merge branch 'purchasing-job' into production
# Conflicts: # indoteknik_custom/models/__init__.py # indoteknik_custom/models/sale_order.py
Diffstat (limited to 'indoteknik_custom/models/automatic_purchase.py')
-rw-r--r--indoteknik_custom/models/automatic_purchase.py458
1 files changed, 398 insertions, 60 deletions
diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py
index ff566aa9..3e2d31e7 100644
--- a/indoteknik_custom/models/automatic_purchase.py
+++ b/indoteknik_custom/models/automatic_purchase.py
@@ -1,7 +1,7 @@
from odoo import models, api, fields
from odoo.exceptions import UserError
from datetime import datetime
-import logging
+import logging, math
_logger = logging.getLogger(__name__)
@@ -9,16 +9,59 @@ _logger = logging.getLogger(__name__)
class AutomaticPurchase(models.Model):
_name = 'automatic.purchase'
_order = 'id desc'
+ _inherit = ['mail.thread']
+ _rec_name = 'number'
- date_doc = fields.Date(string='Date', required=True, help='Isi tanggal hari ini')
+ number = fields.Char(string='Document No', index=True, copy=False, readonly=True)
+ date_doc = fields.Date(string='Date', readonly=True, help='Isi tanggal hari ini')
description = fields.Char(string='Description', help='bebas isi nya apa')
purchase_lines = fields.One2many('automatic.purchase.line', 'automatic_purchase_id', string='Lines', auto_join=True)
notification = fields.Char(string='Notification')
is_po = fields.Boolean(string='Is PO')
- purchase_match = fields.One2many('automatic.purchase.match', 'automatic_purchase_id', string='Matches', auto_join=True)
+ purchase_match = fields.One2many('automatic.purchase.match', 'automatic_purchase_id', string='PO Matches', auto_join=True)
vendor_id = fields.Many2one('res.partner', string='Vendor', help='boleh kosong, jika diisi, maka hanya keluar data untuk vendor tersebut')
- responsible_id = fields.Many2one('res.users', string='Responsible', required=True)
+ responsible_id = fields.Many2one('res.users', string='Responsible', readonly=True)
+ apo_type = fields.Selection([
+ ('regular', 'Regular Fulfill SO'),
+ ('reordering', 'Reordering Rule'),
+ ], string='Type', tracking=3)
+ sales_match = fields.One2many('automatic.purchase.sales.match', 'automatic_purchase_id', string='SO Matches', auto_join=True)
+ purchasing_job_match = fields.One2many('sync.purchasing.job', 'automatic_purchase_id', string='Purchasing Job Matches', auto_join=True)
+ total_qty_line = fields.Float(string='Total Qty Line', compute='_compute_total_qty')
+ total_qty_so = fields.Float(string='Total Qty SO', compute='_compute_total_qty')
+ def _compute_total_qty(self):
+ for data in self:
+ qty_line = qty_so = 0
+ for line in self.purchase_lines:
+ qty_line = qty_line + line.qty_purchase
+ for line in self.sales_match:
+ qty_so = qty_so + line.qty_so
+ data.total_qty_line = qty_line
+ data.total_qty_so = qty_so
+
+ @api.model
+ def create(self, vals):
+ vals['number'] = self.env['ir.sequence'].next_by_code('automatic.purchase') or '0'
+ result = super(AutomaticPurchase, self).create(vals)
+ return result
+
+ def validate_so_is_po(self):
+ # TODO user can't create po if in so exist matches po
+ sale_ids = self.env['automatic.purchase.sales.match'].search([
+ ('automatic_purchase_id', '=', self.id),
+ ])
+
+ names = []
+ for sale in sale_ids:
+ names.append(sale.sale_id.name)
+
+ count = len(sale_ids)
+
+ if count > 0:
+ raise UserError('Ada sekitar %s SO Yang sudah create PO, berikut SO nya: %s' % (count, ', '.join(names)))
+
+
def create_po_from_automatic_purchase(self):
if not self.purchase_lines:
raise UserError('Tidak ada Lines, belum bisa create PO')
@@ -26,71 +69,327 @@ class AutomaticPurchase(models.Model):
raise UserError('Sudah pernah di create PO')
current_time = datetime.now()
- vendor_ids = self.env['automatic.purchase.line'].read_group([('automatic_purchase_id', '=', self.id), ('partner_id', '!=', False)], fields=['partner_id'], groupby=['partner_id'])
-
- counter_po_number = 0
+ vendor_ids = self.env['automatic.purchase.line'].read_group(
+ [('automatic_purchase_id', '=', self.id), ('partner_id', '!=', False)],
+ fields=['partner_id'],
+ groupby=['partner_id']
+ )
+
+ counter = 0
for vendor in vendor_ids:
- param_header = {
- 'partner_id': vendor['partner_id'][0],
- # 'partner_ref': 'Automatic PO',
- '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,
- 'note_description': 'Automatic PO'
- }
+ self.create_po_by_vendor(vendor['partner_id'][0])
+
+ # param_header = {
+ # 'partner_id': vendor['partner_id'][0],
+ # '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,
+ # 'note_description': 'Automatic PO'
+ # }
+
+ # products_vendors = self.env['automatic.purchase.line'].search([
+ # ('automatic_purchase_id', '=', self.id),
+ # ('partner_id', '=', vendor['partner_id'][0]),
+ # ('qty_purchase', '>', 0)
+ # ], order='brand_id')
+ # counter += 1
# new_po = self.env['purchase.order'].create([param_header])
- products_vendors = self.env['automatic.purchase.line'].search([
- ('automatic_purchase_id', '=', self.id),
- ('partner_id', '=', vendor['partner_id'][0]),
- ('qty_purchase', '>', 0)
- ], order='brand_id')
- count = brand_id = 0
- for product in products_vendors:
- if not product.taxes_id:
- raise UserError('Tidak ada Taxes')
- if count == 200 or brand_id != product.brand_id.id:
- count = 0
- counter_po_number += 1
- new_po = self.env['purchase.order'].create([param_header])
- new_po.name = new_po.name + "/A/"+str(counter_po_number)
- self.env['automatic.purchase.match'].create([{
- 'automatic_purchase_id': self.id,
- 'order_id': new_po.id
- }])
- self.env.cr.commit()
- # else:
- # new_po = self.env['purchase.order'].create([param_header])
- brand_id = product.brand_id.id
- count += 10
-
- qty_available = product.product_id.qty_onhand_bandengan + product.product_id.qty_incoming_bandengan - product.product_id.outgoing_qty
- suggest = 'harus beli'
- if qty_available > product.qty_purchase:
- suggest = 'masih cukup'
+ # new_po.name = new_po.name + "/A/" + str(counter)
+ # self.env['automatic.purchase.match'].create([{
+ # 'automatic_purchase_id': self.id,
+ # 'order_id': new_po.id
+ # }])
+ # self.env.cr.commit()
+
+ # count = 0
+ # for product in products_vendors:
+ # if count == 20:
+ # self.create_purchase_order_sales_match(new_po)
+
+ # if count == 20:
+ # counter += 1
+ # new_po = self.env['purchase.order'].create([param_header])
+ # new_po.name = new_po.name + "/B/" + str(counter)
+ # self.env['automatic.purchase.match'].create([{
+ # 'automatic_purchase_id': self.id,
+ # 'order_id': new_po.id
+ # }])
+
+ # self.create_purchase_order_sales_match(new_po)
+ # self.env.cr.commit()
+
+ # count += 1
+ # qty_available = (
+ # product.product_id.qty_onhand_bandengan +
+ # product.product_id.qty_incoming_bandengan -
+ # product.product_id.outgoing_qty
+ # )
+ # suggest = 'harus beli' if qty_available <= product.qty_purchase else 'masih cukup'
+ # param_line = {
+ # 'order_id': new_po.id,
+ # 'product_id': product.product_id.id,
+ # 'product_qty': product.qty_purchase,
+ # 'qty_available_store': qty_available,
+ # 'suggest': suggest,
+ # 'product_uom_qty': product.qty_purchase,
+ # 'price_unit': product.last_price,
+ # }
+ # new_line = self.env['purchase.order.line'].create([param_line])
+ # product.current_po_id = new_po.id
+ # product.current_po_line_id = new_line.id
+
+ # self.create_purchase_order_sales_match(new_po)
+
+ self.notification = 'PO Created successfully'
+ self.is_po = True
+
+ def create_po_by_vendor(self, vendor_id):
+ current_time = datetime.now()
+
+ PRODUCT_PER_PO = 20
+
+ auto_purchase_line = self.env['automatic.purchase.line']
+
+ last_po = self.env['purchase.order'].search([
+ ('partner_id', '=', vendor_id),
+ ('state', '=', 'done'),
+ ], order='id desc', limit=1)
+
+ param_header = {
+ 'partner_id': vendor_id,
+ 'currency_id': 12,
+ 'user_id': last_po.user_id.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'
+ }
+
+ domain = [
+ ('automatic_purchase_id', '=', self.id),
+ ('partner_id', '=', vendor_id),
+ ('qty_purchase', '>', 0)
+ ]
+
+ products_len = auto_purchase_line.search_count(domain)
+ page = math.ceil(products_len / PRODUCT_PER_PO)
+
+ # 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 + "/A/" + str(i + 1)
+
+ self.env['automatic.purchase.match'].create([{
+ 'automatic_purchase_id': self.id,
+ 'order_id': new_po.id
+ }])
+
+ lines = auto_purchase_line.search(
+ domain,
+ offset=i * PRODUCT_PER_PO,
+ limit=PRODUCT_PER_PO
+ )
+
+ 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,
- 'sequence': count,
- 'product_id': product.product_id.id,
- 'product_qty': product.qty_purchase,
- 'qty_available_store': qty_available,
- 'suggest': suggest,
- 'product_uom_qty': product.qty_purchase,
- 'taxes_id': [product.taxes_id.id],
- 'price_unit': product.last_price,
+ '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,
+ # 'so_line_id': [sales.sale_line_id.id for sales in sales_match],
}
- new_line = self.env['purchase.order.line'].create([param_line])
- product.current_po_id = new_po.id
- product.current_po_line_id = new_line.id
- _logger.info('Automatic Create PO Line %s' % product.product_id.name)
- self.notification = self.notification + ' %s' % new_po.name
- self.is_po = True
-
+ 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)
+
+
+ def create_purchase_order_sales_match(self, purchase_order):
+ matches_so_product_ids = [line.product_id.id for line in purchase_order.order_line]
+
+ matches_so = self.env['automatic.purchase.sales.match'].search([
+ ('automatic_purchase_id', '=', self.id),
+ ('sale_line_id.product_id', 'in', matches_so_product_ids),
+ ])
+
+ for sale_order in matches_so:
+ matches_so_line = {
+ 'purchase_order_id': purchase_order.id,
+ 'sale_id': sale_order.sale_id.id,
+ 'sale_line_id': sale_order.sale_line_id.id,
+ 'picking_id': sale_order.picking_id.id,
+ 'move_id': sale_order.move_id.id,
+ 'partner_id': sale_order.partner_id.id,
+ 'partner_invoice_id': sale_order.partner_invoice_id.id,
+ 'salesperson_id': sale_order.salesperson_id.id,
+ 'product_id': sale_order.product_id.id,
+ 'qty_so': sale_order.qty_so,
+ 'qty_po': sale_order.qty_po,
+ }
+ po_matches_so_line = self.env['purchase.order.sales.match'].create([matches_so_line])
+
+ self.create_sales_order_purchase_match(purchase_order)
+
+ def create_sales_order_purchase_match(self, purchase_order):
+ #TODO add matches po to sales order by automatic_purchase.sales.match
+ matches_po_product_ids = [line.product_id.id for line in purchase_order.order_line]
+
+ sales_match_line = self.env['automatic.purchase.sales.match'].search([
+ ('automatic_purchase_id', '=', self.id),
+ ('sale_line_id.product_id', 'in', matches_po_product_ids),
+ ])
+
+ for sales_match in sales_match_line:
+ purchase_line = self.env['automatic.purchase.line'].search([
+ ('automatic_purchase_id', '=', self.id),
+ ('product_id', 'in', [sales_match.product_id.id]),
+ ])
+
+ matches_po_line = {
+ 'sales_order_id' : sales_match.sale_id.id,
+ 'purchase_order_id' : purchase_line.current_po_id.id,
+ 'purchase_line_id' : purchase_line.current_po_line_id.id,
+ 'product_id' : sales_match.product_id.id,
+ 'qty_so' : sales_match.qty_so,
+ 'qty_po' : sales_match.qty_po,
+ }
+
+ sales_order_purchase_match = self.env['sales.order.purchase.match'].create([matches_po_line])
+
+ def generate_regular_purchase(self):
+ po = self.env['purchase.order'].search([
+ ('state', '=', 'draft')
+ ])
+ if po:
+ raise UserError('Ada PO yang statusnya draft, proses dulu')
+ if self.apo_type == 'reordering':
+ raise UserError('Tombol ini hanya untuk Regular Fulfill SO')
+ if self.vendor_id:
+ raise UserError('Vendor tidak dapat diisi jika Regular Fulfill SO')
+ if self.purchase_lines:
+ raise UserError('Sudah digenerate sebelumnya, hapus line terlebih dahulu')
+
+ self.responsible_id = self.env.user.id
+ self.date_doc = datetime.utcnow()
+
+ #TODO must add order by for fifo mechanism
+ #change the view of v.purchasing.job and use order by in query
+ query = [
+ ('action', '=', 'kurang')
+ ]
+ jobs = self.env['v.purchasing.job'].search(query)
+ count = 0
+ for job in jobs:
+ qty_purchase = job.outgoing - (job.onhand + job.incoming)
+ qty_available = (job.onhand + job.incoming) - job.outgoing
+
+ domain = [
+ ('product_id.id', '=', job.product_id.id),
+ ]
+ orderby = 'count_trx_po desc, count_trx_po_vendor desc'
+ purchase_pricelist = self.env['purchase.pricelist'].search(domain, order=orderby, limit=1)
+
+ vendor_id = purchase_pricelist.vendor_id
+ price, taxes = self._get_valid_purchase_price(purchase_pricelist)
+ last_po_line = self.env['purchase.order.line'].search([('product_id', '=', job.product_id.id), ('order_id.state', '=', 'done')], order='id desc', limit=1)
+
+ self.env['automatic.purchase.line'].create([{
+ 'automatic_purchase_id': self.id,
+ 'product_id': job.product_id.id,
+ 'qty_purchase': qty_purchase,
+ 'qty_available': qty_available,
+ 'partner_id': vendor_id.id,
+ 'last_price': price,
+ 'taxes_id': taxes,
+ 'subtotal': qty_purchase * price,
+ 'last_order_id': last_po_line.order_id.id,
+ 'last_orderline_id': last_po_line.id,
+ 'brand_id': job.product_id.product_tmpl_id.x_manufacture.id
+ }])
+ count += 1
+ _logger.info('Create Automatic Purchase Line %s' % job.product_id.name)
+ self.notification = "Automatic PO Created %s Lines" % count
+ self._create_sales_matching()
+ self._create_sync_purchasing_job(jobs)
+ print(1)
+
+ def _create_sales_matching(self):
+ for line in self.purchase_lines:
+ domain = [
+ ('product_id', '=', line.product_id.id)
+ ]
+ sales = self.env['v.sales.outstanding'].search(domain)
+ for sale in sales:
+ existing_match = self.env['automatic.purchase.sales.match'].search([
+ ('automatic_purchase_id', '=', self.id),
+ ('sale_id', '=', sale.sale_id.id),
+ ])
+
+ if not existing_match:
+ if line.qty_purchase > sale.outgoing:
+ qty_po = sale.outgoing
+ else:
+ qty_po = line.qty_purchase
+
+ self.env['automatic.purchase.sales.match'].create([{
+ 'automatic_purchase_id': self.id,
+ 'sale_id': sale.sale_id.id,
+ 'sale_line_id': sale.sale_line_id.id,
+ 'picking_id': sale.picking_id.id,
+ 'move_id': sale.move_id.id,
+ 'partner_id': sale.partner_id.id,
+ 'partner_invoice_id': sale.partner_invoice_id.id,
+ 'salesperson_id': sale.salesperson_id.id,
+ 'product_id': sale.product_id.id,
+ 'qty_so': sale.outgoing,
+ 'qty_po': qty_po,
+ }])
+
+ print(1)
+
+ def _create_sync_purchasing_job(self, jobs):
+ date = datetime.utcnow()
+ for line in jobs:
+ self.env['sync.purchasing.job'].create([{
+ 'automatic_purchase_id': self.id,
+ 'brand': line.brand,
+ 'item_code': line.item_code,
+ 'product_id': line.product_id.id,
+ 'onhand': line.onhand,
+ 'incoming': line.incoming,
+ 'outgoing': line.outgoing,
+ 'action': line.action,
+ 'date': date
+ }])
+
def generate_automatic_purchase(self):
+ # for reordering rule only
+ if self.apo_type == 'regular':
+ raise UserError('Tombol ini hanya untuk Reordering Rule')
if self.purchase_lines:
raise UserError('Sudah digenerate sebelumnya, hapus line terlebih dahulu')
+ self.responsible_id = self.env.user.id
+
query = [
('product_min_qty', '>', 0),
('product_id.x_manufacture.user_id.id', '=', self.responsible_id.id)
@@ -181,6 +480,11 @@ class AutomaticPurchaseLine(models.Model):
brand_id = fields.Many2one('x_manufactures', string='Brand')
taxes_id = fields.Many2one('account.tax', string='Taxes')
+ @api.onchange('last_price', 'qty_purchase')
+ def _calculate_subtotal(self):
+ for line in self:
+ line.subtotal = line.qty_purchase * line.last_price
+
class AutomaticPurchaseMatch(models.Model):
_name = 'automatic.purchase.match'
@@ -195,3 +499,37 @@ class AutomaticPurchaseMatch(models.Model):
for match in self:
match.vendor = match.order_id.partner_id.name
match.total = match.order_id.amount_total
+
+
+class AutomaticPurchaseSalesMatch(models.Model):
+ _name = 'automatic.purchase.sales.match'
+ _order = 'automatic_purchase_id, id'
+
+ automatic_purchase_id = fields.Many2one('automatic.purchase', string='Ref', required=True, ondelete='cascade', index=True, copy=False)
+ automatic_purchase_line_id = fields.Many2one('automatic.purchase.line', string='Automatic Line')
+ sale_id = fields.Many2one('sale.order', string='SO')
+ sale_line_id = fields.Many2one('sale.order.line', string='SO Line')
+ picking_id = fields.Many2one('stock.picking', string='Picking')
+ move_id = fields.Many2one('stock.move', string='Move')
+ partner_id = fields.Many2one('res.partner', string='Partner')
+ partner_invoice_id = fields.Many2one('res.partner', string='Invoice Partner')
+ salesperson_id = fields.Many2one('res.users', string='Sales')
+ product_id = fields.Many2one('product.product', string='Product')
+ qty_so = fields.Float(string='Qty SO')
+ qty_po = fields.Float(string='Qty PO')
+
+
+class SyncPurchasingJob(models.Model):
+ _name = 'sync.purchasing.job'
+ _order = 'automatic_purchase_id, id'
+
+ automatic_purchase_id = fields.Many2one('automatic.purchase', string='Ref', required=True, ondelete='cascade', index=True, copy=False)
+ product_id = fields.Many2one('product.product', string="Product")
+ brand = fields.Char(string='Brand')
+ item_code = fields.Char(string='Item Code')
+ product = fields.Char(string='Product Name')
+ onhand = fields.Float(string='OnHand')
+ incoming = fields.Float(string="Incoming")
+ outgoing = fields.Float(string="Outgoing")
+ action = fields.Char(string="Status")
+ date = fields.Datetime(string="Date Sync")