summaryrefslogtreecommitdiff
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
parentdb1780524b1e153338ced116f786b4d712d66aca (diff)
parentb2377426bec8aa334277aac48b0b25f0dfac420f (diff)
Merge branch 'purchasing-job' into production
# Conflicts: # indoteknik_custom/models/__init__.py # indoteknik_custom/models/sale_order.py
-rwxr-xr-xindoteknik_custom/__manifest__.py3
-rwxr-xr-xindoteknik_custom/models/__init__.py9
-rw-r--r--indoteknik_custom/models/automatic_purchase.py458
-rwxr-xr-xindoteknik_custom/models/product_template.py5
-rwxr-xr-xindoteknik_custom/models/purchase_order.py59
-rwxr-xr-xindoteknik_custom/models/purchase_order_line.py7
-rw-r--r--indoteknik_custom/models/purchase_order_sales_match.py22
-rw-r--r--indoteknik_custom/models/purchasing_job.py159
-rw-r--r--indoteknik_custom/models/purchasing_job_multi_update.py31
-rw-r--r--indoteknik_custom/models/purchasing_job_state.py17
-rw-r--r--indoteknik_custom/models/report_stock_forecasted.py22
-rwxr-xr-xindoteknik_custom/models/sale_monitoring_detail.py12
-rw-r--r--indoteknik_custom/models/sale_monitoring_detail_v2.py68
-rwxr-xr-xindoteknik_custom/models/sale_order.py14
-rw-r--r--indoteknik_custom/models/sale_order_line.py13
-rw-r--r--indoteknik_custom/models/sales_order_fullfillment.py26
-rw-r--r--indoteknik_custom/models/sales_order_purchase_match.py22
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv11
-rw-r--r--indoteknik_custom/views/automatic_purchase.xml87
-rw-r--r--indoteknik_custom/views/ir_sequence.xml11
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml26
-rw-r--r--indoteknik_custom/views/purchasing_job.xml85
-rw-r--r--indoteknik_custom/views/purchasing_job_multi_update.xml31
-rwxr-xr-xindoteknik_custom/views/sale_monitoring_detail.xml2
-rw-r--r--indoteknik_custom/views/sale_monitoring_detail_v2.xml113
-rwxr-xr-xindoteknik_custom/views/sale_order.xml39
26 files changed, 1256 insertions, 96 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 571b36c3..95c52fc9 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -107,6 +107,9 @@
'views/stock_warehouse_orderpoint.xml',
'views/customer_commision.xml',
'views/wati_history.xml',
+ 'views/purchasing_job.xml',
+ 'views/purchasing_job_multi_update.xml',
+ 'views/sale_monitoring_detail_v2.xml',
'views/purchase_order_multi_update.xml',
'views/purchase_order_multi_confirm.xml',
'views/invoice_reklas_penjualan.xml',
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index 69cbffca..92a0f232 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -93,6 +93,12 @@ from . import account_bank_statement
from . import stock_warehouse_orderpoint
from . import commision
from . import sale_advance_payment_inv
+from . import purchasing_job_state
+from . import purchasing_job
+from . import purchasing_job_multi_update
+from . import purchase_order_sales_match
+from . import sales_order_purchase_match
+from . import sale_monitoring_detail_v2
from . import purchase_order_multi_update
from . import invoice_reklas_penjualan
from . import purchase_order_multi_confirm
@@ -102,4 +108,5 @@ from . import report_logbook_sj
from . import role_permission
from . import cust_commision
from . import report_stock_forecasted
-from . import web_logging \ No newline at end of file
+from . import web_logging
+from . import sales_order_fullfillment
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")
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index ba8fac55..4bab2cad 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -349,6 +349,11 @@ class ProductProduct(models.Model):
sla_version = fields.Integer(string="SLA Version", default=0)
is_edited = fields.Boolean(string='Is Edited')
qty_sold = fields.Float(string='Sold Quantity', compute='_get_qty_sold')
+
+ def _get_po_suggest(self, qty_purchase):
+ if self.qty_available_bandengan < qty_purchase:
+ return 'harus beli'
+ return 'masih cukup'
def _get_qty_upcoming(self):
for product in self:
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index dc654196..1cdf5094 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -16,6 +16,7 @@ _logger = logging.getLogger(__name__)
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
+ order_sales_match_line = fields.One2many('purchase.order.sales.match', 'purchase_order_id', string='Sales Match Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True)
sale_order_id = fields.Many2one('sale.order', string='Sale Order')
procurement_status = fields.Char(string='Procurement Status', compute='get_procurement_status', readonly=True)
po_status = fields.Selection([
@@ -52,6 +53,7 @@ class PurchaseOrder(models.Model):
responsible_ids = fields.Many2many('res.users', string='Responsibles', compute='_compute_responsibles')
status_paid_cbd = fields.Boolean(string='Paid Status', tracking=3, help='Field ini diisi secara manual oleh Finance AP dan hanya untuk status PO CBD')
revisi_po = fields.Boolean(string='Revisi', tracking=3)
+ from_apo = fields.Boolean(string='From APO')
@api.model
def action_multi_cancel(self):
@@ -441,6 +443,10 @@ class PurchaseOrder(models.Model):
return res
def compute_total_margin(self):
+ if self.from_apo:
+ self.compute_total_margin_from_apo()
+ return
+
sum_so_margin = sum_sales_price = sum_margin = 0
for line in self.order_line:
sale_order_line = line.so_line_id
@@ -474,6 +480,59 @@ class PurchaseOrder(models.Model):
self.total_so_margin = 0
self.total_so_percent_margin = 0
+ def compute_total_margin_from_apo(self):
+ purchase_price_dict = {}
+
+ for lines in self.order_line:
+ product_id = lines.product_id.id
+
+ if product_id not in purchase_price_dict:
+ purchase_price_dict[product_id] = lines.price_subtotal
+
+ sum_so_margin = sum_sales_price = sum_margin = 0
+ for line in self.order_sales_match_line:
+ sale_order_line = line.sale_line_id
+
+ if not sale_order_line:
+ sale_order_line = self.env['sale.order.line'].search([
+ ('product_id', '=', line.product_id.id),
+ ('order_id', '=', line.sale_id.id)
+ ], limit=1, order='price_reduce_taxexcl')
+
+ sum_so_margin += sale_order_line.item_margin
+
+ sales_price = sale_order_line.price_reduce_taxexcl * sale_order_line.product_uom_qty
+
+ if sale_order_line.order_id.shipping_cost_covered == 'indoteknik':
+ sales_price -= sale_order_line.delivery_amt_line
+
+ if sale_order_line.order_id.fee_third_party > 0:
+ sales_price -= sale_order_line.fee_third_party_line
+
+ sum_sales_price += sales_price
+
+ product_id = sale_order_line.product_id.id
+
+ purchase_price = purchase_price_dict.get(product_id, 0)
+
+ if line.purchase_order_id.delivery_amount > 0:
+ purchase_price += line.delivery_amt_line
+
+ real_item_margin = sales_price - purchase_price
+ sum_margin += real_item_margin
+
+
+ if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0:
+ self.total_so_margin = sum_so_margin
+ self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100
+ self.total_margin = sum_margin
+ self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100
+ else:
+ self.total_margin = 0
+ self.total_percent_margin = 0
+ self.total_so_margin = 0
+ self.total_so_percent_margin = 0
+
def compute_amt_total_without_service(self):
for order in self:
sum_price_total = 0
diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py
index f1881942..c2e7a4c7 100755
--- a/indoteknik_custom/models/purchase_order_line.py
+++ b/indoteknik_custom/models/purchase_order_line.py
@@ -34,6 +34,8 @@ class PurchaseOrderLine(models.Model):
indent = fields.Boolean(string='Indent', help='centang ini jika barang indent')
is_ltc = fields.Boolean(string='Sudah di LTC', default=False, help='centang ini jika barang sudah di LTC')
note = fields.Char(string='Note')
+ sale_automatic_id = fields.Many2one('sale.order', string='SO')
+
qty_reserved = fields.Float(string='Qty Reserved', compute='_compute_qty_reserved')
delete_line = fields.Boolean(string='Delete', default=False, help='centang ini jika anda ingin menghapus line ini')
is_edit_product_qty = fields.Boolean(string='Is Edit Product Qty', compute='_compute_is_edit_product_qty')
@@ -75,6 +77,11 @@ class PurchaseOrderLine(models.Model):
reserved_qty = sum(line.qty_reserved for line in sale_line)
line.qty_reserved = reserved_qty
+ # so_line.qty_reserved (compute)
+ # so_line.qty_reserved = get from picking_ids where type outgoing and prodid = line.prodid
+
+ # po_line.qty_reserved = cek dulu apakah ada relasi ke sale order. Jika ada maka ambil sesuai yang ada di sale order (so_line.qty_reserved),
+ # jika tidak maka 0
def suggest_purchasing(self):
for line in self:
if line.product_id.qty_available_bandengan + line.qty_reserved < line.product_qty:
diff --git a/indoteknik_custom/models/purchase_order_sales_match.py b/indoteknik_custom/models/purchase_order_sales_match.py
new file mode 100644
index 00000000..02c19b1f
--- /dev/null
+++ b/indoteknik_custom/models/purchase_order_sales_match.py
@@ -0,0 +1,22 @@
+from odoo import fields, models, api, _
+from odoo.exceptions import AccessError, UserError, ValidationError
+from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchaseOrderSalesMatch(models.Model):
+ _name = 'purchase.order.sales.match'
+
+ purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order', index=True, required=True, ondelete='cascade')
+ 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') \ No newline at end of file
diff --git a/indoteknik_custom/models/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py
new file mode 100644
index 00000000..1c74f0e9
--- /dev/null
+++ b/indoteknik_custom/models/purchasing_job.py
@@ -0,0 +1,159 @@
+from odoo import fields, models, api, tools, _
+import logging
+from datetime import datetime
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchasingJob(models.Model):
+ _name = 'v.purchasing.job'
+ _auto = False
+ _rec_name = 'product_id'
+
+ id = fields.Integer()
+ product_id = fields.Many2one('product.product', string="Product")
+ vendor_id = fields.Many2one('res.partner', string="Vendor", compute='compute_vendor_id')
+ 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")
+ status_apo = fields.Selection([
+ ('not_apo', 'Belum APO'),
+ ('apo', 'APO')
+ ], string='APO?')
+
+ def compute_vendor_id(self):
+ for sale in self:
+ domain = [
+ ('product_id', '=', sale.product_id.id)
+ ]
+ sales = self.env['v.sales.outstanding'].search(domain)
+
+ vendor_id = False
+
+ if sales:
+ vendor_id = sales[0].sale_line_id.vendor_id.id
+
+ sale.vendor_id = vendor_id
+
+ def init(self):
+ tools.drop_view_if_exists(self.env.cr, self._table)
+ self.env.cr.execute("""
+ CREATE OR REPLACE VIEW %s AS (
+ SELECT
+ pmp.product_id as id,
+ pmp.product_id,
+ pmp.brand,
+ pmp.item_code,
+ pmp.product,
+ pmp.onhand,
+ pmp.incoming,
+ pmp.outgoing,
+ pmp.action,
+ pjs.status_apo AS status_apo
+ FROM v_procurement_monitoring_by_product pmp
+ LEFT JOIN purchasing_job_state pjs ON pjs.purchasing_job_id = pmp.product_id
+ WHERE pmp.action = 'kurang'
+ )
+ """ % self._table)
+
+
+ def open_form_multi_generate_request_po(self):
+ action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_purchasing_job_multi_update')
+ action['context'] = {
+ 'product_ids': [x.id for x in self]
+ }
+ return action
+
+ def generate_request_po(self):
+ # print(1)
+ # TODO create document automatic purchase
+
+ current_time = datetime.utcnow()
+
+ automatic_purchase = self.env['automatic.purchase'].create([{
+ 'apo_type': 'regular',
+ 'date_doc': current_time,
+ }])
+ count = 0
+ for job in self:
+ print(job.product_id.name)
+ 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 = automatic_purchase._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': automatic_purchase.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
+ }])
+ automatic_purchase._create_sales_matching()
+ automatic_purchase._create_sync_purchasing_job(job)
+ count += 1
+ _logger.info('Create Automatic Purchase Line %s' % job.product_id.name)
+ return automatic_purchase.id
+
+class OutstandingSales(models.Model):
+ _name = 'v.sales.outstanding'
+ _auto = False
+ _rec_name = 'move_id'
+
+ id = fields.Integer()
+ move_id = fields.Many2one('stock.move', string='Move')
+ picking_id = fields.Many2one('stock.picking', string='Picking')
+ product_id = fields.Many2one('product.product', string='Product')
+ sale_id = fields.Many2one('sale.order', string='Sale')
+ sale_line_id = fields.Many2one('sale.order.line', string='Sale Line')
+ 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='Salesperson')
+ origin = fields.Char(string='Origin')
+ salesperson = fields.Char(string='Sales Name')
+ item_code = fields.Char(string='Item Code')
+ product = fields.Char(string='Product')
+ outgoing = fields.Float(string='Outgoing')
+ brand = fields.Char(string='Brand')
+ invoice_partner = fields.Char(string='Invoice Partner')
+
+ def init(self):
+ tools.drop_view_if_exists(self.env.cr, self._table)
+ self.env.cr.execute("""
+ CREATE OR REPLACE VIEW v_sales_outstanding AS (
+ select sm.id, sm.id as move_id, sp.id as picking_id, sm.product_id, so.id as sale_id,
+ sol.id as sale_line_id, rp.id as partner_id, so.user_id as salesperson_id, so.partner_invoice_id,
+ sp.origin, rp2.name as salesperson, coalesce(pp.default_code, pt.default_code) as item_code, pt.name as product,
+ sm.product_uom_qty as outgoing, xm.x_name as brand, rp.name as invoice_partner
+ from stock_move sm
+ join stock_picking sp on sp.id = sm.picking_id
+ join sale_order_line sol on sol.id = sm.sale_line_id
+ join sale_order so on so.id = sol.order_id
+ join res_partner rp on rp.id = so.partner_invoice_id
+ join res_users ru on ru.id = so.user_id
+ join res_partner rp2 on rp2.id = ru.partner_id
+ join product_product pp on pp.id = sm.product_id
+ join product_template pt on pt.id = pp.product_tmpl_id
+ left join x_manufactures xm on xm.id = pt.x_manufacture
+ where sp.state in ('draft', 'waiting', 'confirmed', 'assigned')
+ and sp.name like '%OUT%'
+ )
+ """)
diff --git a/indoteknik_custom/models/purchasing_job_multi_update.py b/indoteknik_custom/models/purchasing_job_multi_update.py
new file mode 100644
index 00000000..e7f19e50
--- /dev/null
+++ b/indoteknik_custom/models/purchasing_job_multi_update.py
@@ -0,0 +1,31 @@
+from odoo import models, fields, _
+import logging
+from odoo.exceptions import AccessError, UserError, ValidationError
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchasingJobMultiUpdate(models.TransientModel):
+ _name = 'purchasing.job.multi.update'
+
+ def save_multi_update_purchasing_job(self):
+ product_ids = self._context['product_ids']
+ products = self.env['v.purchasing.job'].browse(product_ids)
+ for product in products:
+ if product.status_apo == 'apo':
+ raise UserError('Ada Purchase Order yang statusnya APO, proses dulu')
+
+ purchasing_job_state = self.env['purchasing.job.state']
+ purchasing_job_state.create({
+ 'purchasing_job_id': product.id,
+ 'status_apo': 'apo',
+ })
+ apo = products.generate_request_po()
+ return {
+ 'name': _('Automatic Purchase'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'automatic.purchase',
+ 'target': 'current',
+ 'type': 'ir.actions.act_window',
+ 'domain': [('id', '=', apo)],
+ } \ No newline at end of file
diff --git a/indoteknik_custom/models/purchasing_job_state.py b/indoteknik_custom/models/purchasing_job_state.py
new file mode 100644
index 00000000..57fd3db2
--- /dev/null
+++ b/indoteknik_custom/models/purchasing_job_state.py
@@ -0,0 +1,17 @@
+from odoo import fields, models, api, tools, _
+import logging
+from datetime import datetime
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchasingJobState(models.Model):
+ _name = 'purchasing.job.state'
+ _rec_name = 'purchasing_job_id'
+
+ purchasing_job_id = fields.Many2one('purchasing.job', string='Ref')
+ status_apo = fields.Selection([
+ ('not_apo', 'Belum APO'),
+ ('apo', 'APO')
+ ], string='APO?', copy=False)
+
diff --git a/indoteknik_custom/models/report_stock_forecasted.py b/indoteknik_custom/models/report_stock_forecasted.py
index 48a17095..92da13d5 100644
--- a/indoteknik_custom/models/report_stock_forecasted.py
+++ b/indoteknik_custom/models/report_stock_forecasted.py
@@ -6,26 +6,30 @@ class ReplenishmentReport(models.AbstractModel):
@api.model
def _get_report_lines(self, product_template_ids, product_variant_ids, wh_location_ids):
lines = super(ReplenishmentReport, self)._get_report_lines(product_template_ids, product_variant_ids, wh_location_ids)
-
result_dict = {}
for line in lines:
- product_id = line.get('product', {}).get('id')
- query = [('product_id', '=', product_id)]
document_out = line.get('document_out')
order_id = document_out.id if document_out else None
+ product_id = line.get('product', {}).get('id')
+ query = [('product_id', '=', product_id)]
if order_id:
result = self._calculate_result(line)
- result_dict.setdefault(order_id, []).append(result)
+ quantity = line.get('quantity', 0)
+ result_dict.setdefault(order_id, []).append((result, quantity))
for order_id, results in result_dict.items():
- sale_order_lines = self.env['sale.order.line'].search([('order_id', '=', order_id)])
+ sales_order = self.env['sale.order'].browse(order_id)
- concatenated_result = ' ,'.join(results)
+ for result, quantity in results:
+ self.env['sales.order.fullfillment'].create({
+ 'sales_order_id': sales_order.id,
+ 'product_id': product_id,
+ 'reserved_from': result,
+ 'qty_fullfillment': quantity,
+ })
- for sale_order_line in sale_order_lines:
- sale_order_line.reserved_from = concatenated_result
return lines
@@ -40,6 +44,6 @@ class ReplenishmentReport(models.AbstractModel):
else:
return 'Free Stock'
else:
- return 'Not Available'
+ return 'Unfulfilled'
diff --git a/indoteknik_custom/models/sale_monitoring_detail.py b/indoteknik_custom/models/sale_monitoring_detail.py
index 8c35b1cc..ec5e4711 100755
--- a/indoteknik_custom/models/sale_monitoring_detail.py
+++ b/indoteknik_custom/models/sale_monitoring_detail.py
@@ -24,16 +24,16 @@ class SaleMonitoringDetail(models.Model):
qty_reserved = fields.Integer(string="Qty Reserved")
note = fields.Char(string="Note")
vendor_id = fields.Many2one('res.partner', string='Vendor')
+ fullfillment = fields.Char(string="Fullfillment", compute='compute_fullfillment')
- def _compute_vendor(self):
+ def compute_fullfillment(self):
for r in self:
- sale_lines = self.env['sale.order.line'].search([
- ('order_id', '=', r.sale_order_id.id),
+ fullfillment = self.env['sales.order.fullfillment'].search([
+ ('sales_order_id', '=', r.sale_order_id.id),
('product_id', '=', r.product_id.id),
- ])
+ ], limit=1)
- for line in sale_lines:
- r.vendor_id = line.vendor_id.id
+ r.fullfillment = fullfillment.reserved_from
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
diff --git a/indoteknik_custom/models/sale_monitoring_detail_v2.py b/indoteknik_custom/models/sale_monitoring_detail_v2.py
new file mode 100644
index 00000000..1aec2bef
--- /dev/null
+++ b/indoteknik_custom/models/sale_monitoring_detail_v2.py
@@ -0,0 +1,68 @@
+from odoo import fields, models, api, tools
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class SaleMonitoringDetailV2(models.Model):
+ _name = 'sale.monitoring.detail.v2'
+ _auto = False
+ _rec_name = 'sale_order_id'
+
+ id = fields.Integer()
+ sale_order_id = fields.Many2one("sale.order", string="Sale Order")
+ partner_id = fields.Many2one("res.partner", string="Customer")
+ user_id = fields.Many2one("res.users", string="Salesperson")
+ product_id = fields.Many2one("product.product", string="Product")
+ qty_so = fields.Integer(string="Qty SO")
+ qty_po = fields.Integer(string="Qty PO")
+ qty_po_received = fields.Integer(string="Qty PO Received")
+ qty_so_delivered = fields.Integer(string="Qty SO Delivered")
+ qty_so_invoiced = fields.Integer(string="Qty SO Invoiced")
+ date_order = fields.Datetime(string="Date Order")
+ status = fields.Char(string="Status")
+ qty_reserved = fields.Integer(string="Qty Reserved")
+ note = fields.Char(string="Note")
+
+ def init(self):
+ tools.drop_view_if_exists(self.env.cr, self._table)
+ self.env.cr.execute("""
+ CREATE OR REPLACE VIEW %s AS (
+ SELECT
+ *,
+ CASE
+ when qty_so_delivered = qty_so then 'Delivered'
+ when qty_reserved >= qty_so then 'Siap kirim'
+ when qty_po + qty_reserved - qty_po_received < qty_so then 'Belum/Kurang PO'
+ when qty_po_received = 0 then 'Belum terima'
+ when qty_po_received < qty_po then 'Terima sebagian'
+ when qty_so_invoiced = qty_so then 'Invoiced'
+ END AS status
+ FROM
+ (
+ SELECT
+ sol.id AS id,
+ so.id AS sale_order_id,
+ so.partner_id as partner_id,
+ so.user_id,
+ p.id AS product_id,
+ sol.product_uom_qty AS qty_so,
+ sol.qty_delivered AS qty_so_delivered,
+ get_qty_to_delivered(sol.id) as qty_to_delivered,
+ sol.qty_invoiced AS qty_so_invoiced,
+ so.date_order AS date_order,
+ get_qty_po_matches_so(so.id, sol.product_id) AS qty_po,
+ get_qty_received(so.id, sol.product_id) AS qty_po_received,
+ get_qty_reserved(so.id, sol.product_id) as qty_reserved,
+ sol.note_procurement as note
+ FROM sale_order so
+ JOIN sale_order_line sol ON sol.order_id = so.id
+ JOIN product_product p ON p.id = sol.product_id
+ JOIN product_template pt ON pt.id = p.product_tmpl_id
+ WHERE pt.type IN ('consu','product')
+ AND so.state IN ('sale','done')
+ AND so.create_date >= '2022-08-10'
+ and so.so_status not in('terproses')
+ ) a
+ )
+ """ % self._table)
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index b7578da0..f9f849ca 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -8,6 +8,9 @@ _logger = logging.getLogger(__name__)
class SaleOrder(models.Model):
_inherit = "sale.order"
+
+ fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment')
+ order_sales_match_line = fields.One2many('sales.order.purchase.match', 'sales_order_id', string='Purchase Match Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True)
total_margin = fields.Float('Total Margin', compute='_compute_total_margin', help="Total Margin in Sales Order Header")
total_percent_margin = fields.Float('Total Percent Margin', compute='_compute_total_percent_margin', help="Total % Margin in Sales Order Header")
approval_status = fields.Selection([
@@ -93,6 +96,17 @@ class SaleOrder(models.Model):
('cust_manager', 'Customer Manager'),
('cust_director', 'Customer Director')
], string='Web Approval', copy=False)
+ compute_fullfillment = fields.Boolean(string='Compute Fullfillment', compute="_compute_fullfillment")
+
+ def _compute_fullfillment(self):
+ for rec in self:
+ for fullfillment in rec.fullfillment_line:
+ fullfillment.unlink()
+
+ for line in rec.order_line:
+ line._compute_reserved_from()
+
+ rec.compute_fullfillment = True
def _compute_eta_date(self):
max_leadtime = 0
diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index a140468c..39366028 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -13,7 +13,7 @@ class SaleOrderLine(models.Model):
change_default=True, index=True, tracking=1,
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
- )
+ )
purchase_price = fields.Float('Purchase', required=True, digits='Product Price', default=0.0)
purchase_tax_id = fields.Many2one('account.tax', string='Tax', domain=['|', ('active', '=', False), ('active', '=', True)])
delivery_amt_line = fields.Float('DeliveryAmtLine', compute='compute_delivery_amt_line')
@@ -67,15 +67,10 @@ class SaleOrderLine(models.Model):
reserved_qty = sum(move.product_uom_qty for move in stock_moves)
line.qty_reserved = reserved_qty
- if reserved_qty > 0:
- line._compute_reserved_from()
-
-
def _compute_reserved_from(self):
- for line in self:
- # continue
- report_stock_forecasted = self.env['report.stock.report_product_product_replenishment']
- report_stock_forecasted._get_report_data(False, [line.product_id.id])
+ for line in self:
+ report_stock_forecasted = self.env['report.stock.report_product_product_replenishment']
+ report_stock_forecasted._get_report_data(False, [line.product_id.id])
def _compute_vendor_subtotal(self):
for line in self:
diff --git a/indoteknik_custom/models/sales_order_fullfillment.py b/indoteknik_custom/models/sales_order_fullfillment.py
new file mode 100644
index 00000000..ab416e8d
--- /dev/null
+++ b/indoteknik_custom/models/sales_order_fullfillment.py
@@ -0,0 +1,26 @@
+from odoo import fields, models, api, _
+from odoo.exceptions import AccessError, UserError, ValidationError
+from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class SalesOrderFullfillment(models.Model):
+ _name = 'sales.order.fullfillment'
+
+ sales_order_id = fields.Many2one('sale.order', string='Sale Order')
+ product_id = fields.Many2one('product.product', string='Product')
+ reserved_from = fields.Char(string='Reserved From', copy=False)
+ qty_fullfillment = fields.Float(string='Qty')
+ user_id = fields.Many2one('res.users', string='Responsible', compute='get_user_id')
+
+ def get_user_id(self):
+ for rec in self:
+ po = self.env['purchase.order'].search([('name', '=', rec.reserved_from)], limit=1)
+
+ if po:
+ rec.user_id = po.user_id.id
+ else:
+ rec.user_id = False
+
diff --git a/indoteknik_custom/models/sales_order_purchase_match.py b/indoteknik_custom/models/sales_order_purchase_match.py
new file mode 100644
index 00000000..042d5855
--- /dev/null
+++ b/indoteknik_custom/models/sales_order_purchase_match.py
@@ -0,0 +1,22 @@
+from odoo import fields, models, api, _
+from odoo.exceptions import AccessError, UserError, ValidationError
+from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class SalesOrderPurchaseMatch(models.Model):
+ _name = 'sales.order.purchase.match'
+
+ sales_order_id = fields.Many2one('sale.order', string='Sale Order', index=True, required=True, ondelete='cascade')
+ purchase_order_id = fields.Many2one('purchase.order', string='PO')
+ purchase_line_id = fields.Many2one('purchase.order.line', string='PO Line')
+ product_id = fields.Many2one('product.product', string='Product')
+ qty_so = fields.Float(string='Qty SO')
+ qty_po = fields.Float(string='Qty PO')
+ # 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') \ No newline at end of file
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 65bd6e90..6d265748 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -86,6 +86,15 @@ access_customer_commision_line,access.customer.commision.line,model_customer_com
access_customer_rebate,access.customer.rebate,model_customer_rebate,,1,1,1,1
access_wati_history,access.wati.history,model_wati_history,,1,1,1,1
access_wati_history_line,access.wati.history.line,model_wati_history_line,,1,1,1,1
+access_v_purchasing_job,access.v.purchasing.job,model_v_purchasing_job,,1,1,1,1
+access_sale_advance_payment_inv,access.sale.advance.payment.inv,model_sale_advance_payment_inv,,1,1,1,1
+access_purchasing_job_multi_update,access.purchasing.job.multi.update,model_purchasing_job_multi_update,,1,1,1,1
+access_automatic_purchase_sales_match,access.automatic.purchase.sales.match,model_automatic_purchase_sales_match,,1,1,1,1
+access_v_sales_outstanding,access.v.sales.outstanding,model_v_sales_outstanding,,1,1,1,1
+access_purchase_order_sales_match,access.purchase.order.sales.match,model_purchase_order_sales_match,,1,1,1,1
+access_sales_order_purchase_match,access.sale.order.purchase.match,model_sales_order_purchase_match,,1,1,1,1
+access_sale_monitoring_detail_v2,access.sale.monitoring.detail.v2,model_sale_monitoring_detail_v2,,1,1,1,1
+access_sync_purchasing_job,access.sync.purchasing.job,model_sync_purchasing_job,,1,1,1,1
access_sale_advance_payment_inv,access.sale.advance.payment.inv,model_sale_advance_payment_inv,,1,1,1,1
access_purchase_order_multi_update,access.purchase.order.multi_update,model_purchase_order_multi_update,,1,1,1,1
access_invoice_reklas_penjualan,access.invoice.reklas.penjualan,model_invoice_reklas_penjualan,,1,1,1,1
@@ -99,3 +108,5 @@ access_report_logbook_sj_line,access.report.logbook.sj.line,model_report_logbook
access_report_logbook_sj_line,access.report.logbook.sj.line,model_report_logbook_sj_line,,1,1,1,1
access_cust_commision,access.cust.commision,model_cust_commision,,1,1,1,1
access_report_stock_report_product_product_replenishment,access.report.stock.report_product_product_replenishment,model_report_stock_report_product_product_replenishment,,1,1,1,1
+access_sales_order_fullfillment,access.sales.order.fullfillment,model_sales_order_fullfillment,,1,1,1,1
+access_purchasing_job_state,access.purchasing.job.state,model_purchasing_job_state,,1,1,1,1 \ No newline at end of file
diff --git a/indoteknik_custom/views/automatic_purchase.xml b/indoteknik_custom/views/automatic_purchase.xml
index 245fda90..17abd888 100644
--- a/indoteknik_custom/views/automatic_purchase.xml
+++ b/indoteknik_custom/views/automatic_purchase.xml
@@ -5,12 +5,14 @@
<field name="model">automatic.purchase</field>
<field name="arch" type="xml">
<tree>
- <field name="date_doc"/>
+ <field name="number"/>
+ <field name="date_doc" readonly="1"/>
+ <field name="apo_type"/>
<field name="vendor_id"/>
<field name="description"/>
<field name="notification" readonly="1"/>
<field name="is_po" readonly="1"/>
- <field name="responsible_id"/>
+ <field name="responsible_id" readonly="1"/>
</tree>
</field>
</record>
@@ -19,7 +21,7 @@
<field name="name">automatic.purchase.line.tree</field>
<field name="model">automatic.purchase.line</field>
<field name="arch" type="xml">
- <tree>
+ <tree editable="bottom">
<field name="brand_id"/>
<field name="product_id"/>
<field name="taxes_id"/>
@@ -27,11 +29,49 @@
<field name="qty_min"/>
<field name="qty_max"/>
<field name="qty_available"/>
+ <field name="qty_purchase"/>
<field name="partner_id"/>
<field name="last_price"/>
+ <field name="taxes_id"/>
<field name="subtotal"/>
- <field name="last_order_id" readonly="1"/>
- <field name="current_po_line_id" readonly="1"/>
+ <field name="last_order_id" readonly="1" optional="hide"/>
+ <field name="current_po_line_id" readonly="1" optional="hide"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="automatic_purchase_sales_match_tree" model="ir.ui.view">
+ <field name="name">automatic.purchase.sales.match.tree</field>
+ <field name="model">automatic.purchase.sales.match</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="sale_id" readonly="1"/>
+ <field name="sale_line_id" readonly="1" optional="hide"/>
+ <field name="picking_id" readonly="1" optional="hide"/>
+ <field name="move_id" readonly="1" optional="hide"/>
+ <field name="partner_id" readonly="1" optional="hide"/>
+ <field name="partner_invoice_id" readonly="1"/>
+ <field name="salesperson_id" readonly="1"/>
+ <field name="product_id" readonly="1"/>
+ <field name="qty_so" readonly="1"/>
+ <field name="qty_po" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="sync_purchasing_job_tree" model="ir.ui.view">
+ <field name="name">sync.purchasing.job.tree</field>
+ <field name="model">sync.purchasing.job</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="product_id" readonly="1"/>
+ <field name="brand" readonly="1" optional="hide"/>
+ <field name="item_code" readonly="1" optional="hide"/>
+ <field name="onhand" readonly="1" optional="hide"/>
+ <field name="incoming" readonly="1"/>
+ <field name="outgoing" readonly="1"/>
+ <field name="action" readonly="1"/>
+ <field name="date" readonly="1"/>
</tree>
</field>
</record>
@@ -57,19 +97,29 @@
<div class="oe_button_box" name="button_box"/>
<group>
<group>
- <field name="date_doc"/>
+ <field name="number"/>
+ <field name="apo_type" required="1"/>
<field name="vendor_id"/>
- <field name="responsible_id"/>
<field name="description"/>
- <field name="notification" readonly="1"/>
</group>
<group>
+ <field name="date_doc"/>
+ <field name="responsible_id"/>
<div>
<button name="generate_automatic_purchase"
- string="Generate Line"
+ string="Generate Reordering"
+ type="object"
+ class="mr-2 oe_highlight"
+ />
+ </div>
+ <div>
+ <button name="generate_regular_purchase"
+ string="Generate Fulfillment"
type="object"
class="mr-2 oe_highlight"
/>
+ </div>
+ <div>
<button name="create_po_from_automatic_purchase"
string="Create PO"
type="object"
@@ -82,11 +132,28 @@
<page string="Lines">
<field name="purchase_lines"/>
</page>
- <page string="Matches">
+ <page string="Matches SO">
+ <field name="sales_match"/>
+ </page>
+ <page string="Matches PO">
<field name="purchase_match"/>
</page>
+ <page string="Matches Purchasing">
+ <field name="purchasing_job_match"/>
+ </page>
+ <page string="Info" name="automatic_purchase_info">
+ <group>
+ <field name="notification" readonly="1"/>
+ <field name="total_qty_line"/>
+ <field name="total_qty_so"/>
+ </group>
+ </page>
</notebook>
</sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
</form>
</field>
</record>
diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml
index 56921839..d52f55ca 100644
--- a/indoteknik_custom/views/ir_sequence.xml
+++ b/indoteknik_custom/views/ir_sequence.xml
@@ -80,5 +80,16 @@
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
+
+ <record id="sequence_automatic_purchase" model="ir.sequence">
+ <field name="name">Automatic Purchase</field>
+ <field name="code">automatic.purchase</field>
+ <field name="active">TRUE</field>
+ <field name="prefix">APO/%(year)s/</field>
+ <field name="padding">5</field>
+ <field name="number_next">1</field>
+ <field name="number_increment">1</field>
+ </record>
+
</data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index c7fdfcc3..baff620c 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -110,6 +110,11 @@
<field name="purchase_order_lines"/>
</page>
</xpath>
+ <xpath expr="//form/sheet/notebook/page[@name='purchase_delivery_invoice']" position="after">
+ <page string="Matches SO" name="purchase_order_sales_matches_lines">
+ <field name="order_sales_match_line"/>
+ </page>
+ </xpath>
</field>
</record>
</data>
@@ -195,6 +200,27 @@
</field>
</record>
</data>
+
+ <data>
+ <record id="purchase_order_sales_matches_tree" model="ir.ui.view">
+ <field name="name">purchase.order.sales.matches.tree</field>
+ <field name="model">purchase.order.sales.match</field>
+ <field name="arch" type="xml">
+ <tree editable="top" create="false" delete="false">
+ <field name="sale_id" readonly="1"/>
+ <field name="sale_line_id" readonly="1" optional="hide"/>
+ <field name="picking_id" readonly="1" optional="hide"/>
+ <field name="move_id" readonly="1" optional="hide"/>
+ <field name="partner_id" readonly="1" optional="hide"/>
+ <field name="partner_invoice_id" readonly="1"/>
+ <field name="salesperson_id" readonly="1"/>
+ <field name="product_id" readonly="1"/>
+ <field name="qty_so" readonly="1"/>
+ <field name="qty_po" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+ </data>
<data>
<record id="purchase_order_multi_update_ir_actions_server" model="ir.actions.server">
<field name="name">Update Paid Status</field>
diff --git a/indoteknik_custom/views/purchasing_job.xml b/indoteknik_custom/views/purchasing_job.xml
new file mode 100644
index 00000000..c439dc8f
--- /dev/null
+++ b/indoteknik_custom/views/purchasing_job.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+ <record id="v_purchasing_job_tree" model="ir.ui.view">
+ <field name="name">v.purchasing.job.tree</field>
+ <field name="model">v.purchasing.job</field>
+ <field name="arch" type="xml">
+ <tree create="false" multi_edit="1">
+ <field name="product_id"/>
+ <field name="vendor_id"/>
+ <field name="brand"/>
+ <field name="item_code"/>
+ <field name="product"/>
+ <field name="onhand"/>
+ <field name="incoming"/>
+ <field name="outgoing"/>
+ <field name="status_apo"/>
+ <field name="action"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="v_purchasing_job_form" model="ir.ui.view">
+ <field name="name">v.purchasing.job.form</field>
+ <field name="model">v.purchasing.job</field>
+ <field name="arch" type="xml">
+ <form create="false" edit="false">
+ <sheet>
+ <group>
+ <group>
+ <field name="product_id"/>
+ <field name="brand"/>
+ <field name="item_code"/>
+ <field name="product"/>
+ <field name="action"/>
+ </group>
+ <group>
+ <field name="onhand"/>
+ <field name="incoming"/>
+ <field name="outgoing"/>
+ </group>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="view_purchasing_job_filter" model="ir.ui.view">
+ <field name="name">v.purchasing.job.list.select</field>
+ <field name="model">v.purchasing.job</field>
+ <field name="priority" eval="15"/>
+ <field name="arch" type="xml">
+ <search string="Search">
+ <field name="product_id"/>
+ <field name="item_code"/>
+ <field name="brand"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="v_purchasing_job_action" model="ir.actions.act_window">
+ <field name="name">Purchasing Job</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">v.purchasing.job</field>
+ <field name="search_view_id" ref="view_purchasing_job_filter"/>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <record id="purchasing_job_multi_update_ir_actions_server" model="ir.actions.server">
+ <field name="name">Generate Automatic PO</field>
+ <field name="model_id" ref="model_v_purchasing_job"/>
+ <field name="binding_model_id" ref="model_v_purchasing_job"/>
+ <field name="binding_view_types">form,list</field>
+ <field name="state">code</field>
+ <field name="code">action = records.open_form_multi_generate_request_po()</field>
+ </record>
+
+ <menuitem
+ id="menu_purchasing_job"
+ name="Purchasing Job"
+ parent="menu_monitoring_in_purchase"
+ sequence="201"
+ action="v_purchasing_job_action"
+ />
+
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/purchasing_job_multi_update.xml b/indoteknik_custom/views/purchasing_job_multi_update.xml
new file mode 100644
index 00000000..79b935c7
--- /dev/null
+++ b/indoteknik_custom/views/purchasing_job_multi_update.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <data>
+ <record id="view_purchasing_job_multi_update_form" model="ir.ui.view">
+ <field name="name">View Purchasing Job Multi Update</field>
+ <field name="model">purchasing.job.multi.update</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <group>
+ <span>Apakah Anda Yakin Ingin Create Automatic PO?</span>
+ </group>
+ </sheet>
+ <footer>
+ <button name="save_multi_update_purchasing_job" string="Create" type="object" default_focus="1" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_purchasing_job_multi_update" model="ir.actions.act_window">
+ <field name="name">Action Purchasing Job Multi Update</field>
+ <field name="res_model">purchasing.job.multi.update</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_purchasing_job_multi_update_form"/>
+ <field name="target">new</field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/sale_monitoring_detail.xml b/indoteknik_custom/views/sale_monitoring_detail.xml
index 5091fb83..ed2bd06f 100755
--- a/indoteknik_custom/views/sale_monitoring_detail.xml
+++ b/indoteknik_custom/views/sale_monitoring_detail.xml
@@ -25,6 +25,7 @@
decoration-info="status == 'Delivered' or status == 'Invoiced'"
/>
<field name="note" optional="hide"/>
+ <field name="fullfillment"/>
</tree>
</field>
</record>
@@ -58,6 +59,7 @@
<field name="qty_po_received"/>
<field name="qty_so_delivered"/>
<field name="qty_so_invoiced"/>
+ <field name="fullfillment"/>
</group>
</group>
</sheet>
diff --git a/indoteknik_custom/views/sale_monitoring_detail_v2.xml b/indoteknik_custom/views/sale_monitoring_detail_v2.xml
new file mode 100644
index 00000000..c2e7844d
--- /dev/null
+++ b/indoteknik_custom/views/sale_monitoring_detail_v2.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+ <record id="sale_monitoring_detail_v2_tree" model="ir.ui.view">
+ <field name="name">sale.monitoring.detail.v2.tree</field>
+ <field name="model">sale.monitoring.detail.v2</field>
+ <field name="arch" type="xml">
+ <tree create="false" multi_edit="1">
+ <field name="date_order"/>
+ <field name="sale_order_id"/>
+ <field name="partner_id"/>
+ <field name="user_id"/>
+ <field name="product_id"/>
+ <field name="qty_so"/>
+ <field name="qty_reserved"/>
+ <field name="qty_po"/>
+ <field name="qty_po_received"/>
+ <field name="qty_so_delivered"/>
+ <field name="qty_so_invoiced"/>
+ <field name="status"
+ widget="badge"
+ decoration-danger="status == 'Belum/Kurang PO'"
+ decoration-warning="status == 'Belum terima' or status == 'Terima sebagian'"
+ decoration-success="status == 'Siap kirim'"
+ decoration-info="status == 'Delivered' or status == 'Invoiced'"
+ />
+ <field name="note" optional="hide"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="sale_monitoring_detail_v2_form" model="ir.ui.view">
+ <field name="name">sale.monitoring.detail.v2.form</field>
+ <field name="model">sale.monitoring.detail.v2</field>
+ <field name="arch" type="xml">
+ <form create="false" edit="false">
+ <sheet>
+ <group>
+ <group>
+ <field name="sale_order_id"/>
+ <field name="partner_id"/>
+ <field name="user_id"/>
+ <field name="product_id"/>
+ <field name="status"
+ widget="badge"
+ decoration-danger="status == 'Belum/Kurang PO'"
+ decoration-warning="status == 'Belum terima' or status == 'Terima sebagian'"
+ decoration-success="status == 'Siap kirim'"
+ decoration-info="status == 'Delivered' or status == 'Invoiced'"
+ />
+ <field name="note"/>
+ </group>
+ <group>
+ <field name="qty_so"/>
+ <field name="qty_reserved"/>
+ <field name="qty_po"/>
+ <field name="qty_po_received"/>
+ <field name="qty_so_delivered"/>
+ <field name="qty_so_invoiced"/>
+ </group>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="view_monitoring_detail_filter" model="ir.ui.view">
+ <field name="name">sale.monitoring.detail.v2.list.select</field>
+ <field name="model">sale.monitoring.detail.v2</field>
+ <field name="priority" eval="15"/>
+ <field name="arch" type="xml">
+ <search string="Search Monitoring">
+ <field name="sale_order_id"/>
+ <field name="partner_id"/>
+ <field name="user_id"/>
+ <field name="product_id"/>
+ <field name="status"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="sale_monitoring_detail_v2_action" model="ir.actions.act_window">
+ <field name="name">Sale Monitoring Detail V2</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">sale.monitoring.detail.v2</field>
+ <field name="search_view_id" ref="view_monitoring_detail_filter"/>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem
+ id="menu_sale_monitoring_detail_v2_in_purchase"
+ name="Sale Detail V2"
+ parent="menu_monitoring_in_purchase"
+ sequence="2"
+ action="sale_monitoring_detail_v2_action"
+ />
+
+ <menuitem
+ id="menu_sale_monitoring_detail_v2_in_sale"
+ name="Sale Detail V2"
+ parent="menu_monitoring_in_sale"
+ sequence="2"
+ action="sale_monitoring_detail_v2_action"
+ />
+
+ <menuitem
+ id="menu_sale_monitoring_detail_v2_in_stock"
+ name="Sale Detail V2"
+ parent="menu_monitoring_in_stock"
+ sequence="2"
+ action="sale_monitoring_detail_v2_action"
+ />
+
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 97b0a598..91475529 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -49,6 +49,7 @@
</field>
<field name="user_id" position="after">
<field name="helper_by_id" readonly="1"/>
+ <field name="compute_fullfillment" invisible="1"/>
</field>
<field name="tag_ids" position="after">
<field name="eta_date" readonly="1"/>
@@ -183,8 +184,13 @@
</form>
</field>
</page>
+ <page string="Matches PO" name="page_matches_po" invisible="1">
+ <field name="order_sales_match_line" readonly="1"/>
+ </page>
+ <page string="Fullfillment" name="page_sale_order_fullfillment">
+ <field name="fullfillment_line" readonly="1"/>
+ </page>
</page>
-
</field>
</record>
</data>
@@ -273,4 +279,35 @@
</field>
</record>
</data>
+
+ <data>
+ <record id="sales_order_purchase_matches_tree" model="ir.ui.view">
+ <field name="name">sale.order.purchase.matches.tree</field>
+ <field name="model">sales.order.purchase.match</field>
+ <field name="arch" type="xml">
+ <tree editable="top" create="false" delete="false">
+ <field name="purchase_order_id" readonly="1"/>
+ <field name="purchase_line_id" readonly="1"/>
+ <field name="product_id" readonly="1"/>
+ <field name="qty_so" readonly="1"/>
+ <field name="qty_po" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+ </data>
+
+ <data>
+ <record id="sales_order_fullfillmet_tree" model="ir.ui.view">
+ <field name="name">sales.order.fullfillment.tree</field>
+ <field name="model">sales.order.fullfillment</field>
+ <field name="arch" type="xml">
+ <tree editable="top" create="false">
+ <field name="product_id" readonly="1"/>
+ <field name="reserved_from" readonly="1"/>
+ <field name="qty_fullfillment" readonly="1"/>
+ <field name="user_id" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+ </data>
</odoo> \ No newline at end of file