summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2023-06-15 15:47:05 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2023-06-15 15:47:05 +0700
commit55c67c71b04fce80c635b3a58d91c8bcb02e17c8 (patch)
treeed8833b969800e36b887b4134c14dab89d3b91c7
parent1f2995a85428ac4335123bd33d48ae17d3c9f36f (diff)
parent24649f8e939484759ef34e5e68f251d951f63c02 (diff)
Merge commit '24649f8e939484759ef34e5e68f251d951f63c02'
Conflicts: indoteknik_custom/__manifest__.py indoteknik_custom/security/ir.model.access.csv
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py5
-rw-r--r--indoteknik_api/models/account_move.py8
-rw-r--r--indoteknik_api/models/product_product.py9
-rw-r--r--indoteknik_api/models/product_template.py1
-rwxr-xr-xindoteknik_custom/__manifest__.py5
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/account_move.py31
-rw-r--r--indoteknik_custom/models/apache_solr.py9
-rw-r--r--indoteknik_custom/models/automatic_purchase.py2
-rwxr-xr-xindoteknik_custom/models/crm_lead.py2
-rw-r--r--indoteknik_custom/models/manufacturing.py4
-rwxr-xr-xindoteknik_custom/models/product_template.py11
-rwxr-xr-xindoteknik_custom/models/purchase_order.py4
-rw-r--r--indoteknik_custom/models/requisition.py179
-rw-r--r--indoteknik_custom/models/res_partner.py7
-rwxr-xr-xindoteknik_custom/models/sale_monitoring.py15
-rwxr-xr-xindoteknik_custom/models/sale_monitoring_detail.py18
-rwxr-xr-xindoteknik_custom/models/sale_order.py104
-rw-r--r--indoteknik_custom/models/stock_picking.py12
-rw-r--r--indoteknik_custom/models/typesense_client.py150
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv8
-rw-r--r--indoteknik_custom/views/ir_sequence.xml10
-rw-r--r--indoteknik_custom/views/landedcost.xml15
-rw-r--r--indoteknik_custom/views/requisition.xml101
-rwxr-xr-xindoteknik_custom/views/sale_monitoring.xml18
-rwxr-xr-xindoteknik_custom/views/sale_monitoring_detail.xml18
-rwxr-xr-xindoteknik_custom/views/sale_order.xml9
-rw-r--r--indoteknik_custom/views/stock_picking.xml10
28 files changed, 699 insertions, 67 deletions
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py
index 2ac92bf9..35361ba4 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -293,6 +293,11 @@ class SaleOrder(controller.Controller):
price = pricelist[price_tier]()
discount_key = 'discount_%s' % price_tier
if price[discount_key] > 0: discount = price[discount_key]
+
+ flashsale = product._get_flashsale_price()
+ flashsale_discount = flashsale['flashsale_discount']
+ if flashsale_discount > 0 and flashsale_discount > discount:
+ discount = flashsale_discount
parameters.append({
'company_id': 1,
diff --git a/indoteknik_api/models/account_move.py b/indoteknik_api/models/account_move.py
index 54e06f84..23a7076c 100644
--- a/indoteknik_api/models/account_move.py
+++ b/indoteknik_api/models/account_move.py
@@ -6,6 +6,10 @@ class AccountMove(models.Model):
_inherit = 'account.move'
def api_v1_single_response(self, account_move, context=False):
+ sale_order = self.env['sale.order'].search([('name', '=', account_move.invoice_origin), ('state', '=', 'done')], limit=1)
+ amount_residual = account_move.amount_residual
+ if sale_order.payment_status == 'settlement' or sale_order.payment_status == 'capture':
+ amount_residual = 0
data = {
'token': self.env['rest.api'].md5_salt(account_move.id, 'account.move'),
'id': account_move.id,
@@ -14,7 +18,7 @@ class AccountMove(models.Model):
'payment_term': account_move.invoice_payment_term_id.name or '',
'sales': account_move.invoice_user_id.name,
'amount_total': account_move.amount_total,
- 'amount_residual': account_move.amount_residual,
+ 'amount_residual': amount_residual,
'invoice_date': account_move.invoice_date.strftime('%d/%m/%Y') or '',
'efaktur': True if account_move.efaktur_document else False,
}
@@ -30,7 +34,7 @@ class AccountMove(models.Model):
'payment_term': account_move.invoice_payment_term_id.name or '',
'sales': account_move.invoice_user_id.name,
'amount_total': account_move.amount_total,
- 'amount_residual': account_move.amount_residual,
+ 'amount_residual': amount_residual,
'invoice_date_due': account_move.invoice_date_due.strftime('%d/%m/%Y') or '',
'customer': res_users.api_address_response(account_move.partner_id),
'products': [],
diff --git a/indoteknik_api/models/product_product.py b/indoteknik_api/models/product_product.py
index 49ea7804..14fe68cb 100644
--- a/indoteknik_api/models/product_product.py
+++ b/indoteknik_api/models/product_product.py
@@ -54,6 +54,13 @@ class ProductProduct(models.Model):
if price[discount_key] > 0: discount_percentage = price[discount_key]
if price[price_key] > 0: price_discount = price[price_key]
+ flashsale = product_product._get_flashsale_price()
+ flashsale_price = flashsale['flashsale_price']
+ flashsale_discount = flashsale['flashsale_discount']
+ if flashsale_price > 0 and flashsale_price < price_discount:
+ price_discount = flashsale_price
+ discount_percentage = flashsale_discount
+
product_template = product_product.product_tmpl_id
data = {
'id': product_product.id,
@@ -139,7 +146,7 @@ class ProductProduct(models.Model):
default_divide_tax = float(1.11)
price_after_disc = self._get_website_price_after_disc()
res = price_after_disc / default_divide_tax
- res = math.floor(res)
+ res = math.ceil(res)
return res
def _get_website_price_after_disc(self):
diff --git a/indoteknik_api/models/product_template.py b/indoteknik_api/models/product_template.py
index c3df97a5..1a345967 100644
--- a/indoteknik_api/models/product_template.py
+++ b/indoteknik_api/models/product_template.py
@@ -59,7 +59,6 @@ class ProductTemplate(models.Model):
'image': self.env['ir.attachment'].api_image('product.template', 'image_128', product_template.id),
'code': product_template.default_code or '',
'name': product_template.name,
- 'lowest_price': self.env['product.pricelist'].get_lowest_product_variant_price(product_template, product_pricelist_default_discount_id),
'variant_total': len(product_template.product_variant_ids),
'stock_total': product_template.qty_stock_vendor,
'weight': product_template.weight,
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index c116ff0c..f21fab0c 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -74,10 +74,15 @@
'views/procurement_monitoring_detail.xml',
'views/product_product.xml',
'views/brand_vendor.xml',
+<<<<<<< HEAD
'views/promotion_program.xml',
'views/promotion_program_line.xml',
'views/promotion_program_free_item.xml',
'views/promotion_program_keyword.xml',
+=======
+ 'views/requisition.xml',
+ 'views/landedcost.xml',
+>>>>>>> 24649f8e939484759ef34e5e68f251d951f63c02
'report/report.xml',
'report/report_banner_banner.xml',
'report/report_banner_banner2.xml',
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index 3123d608..823463ec 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -66,3 +66,4 @@ from . import raja_ongkir
from . import procurement_monitoring_detail
from . import brand_vendor
from . import manufacturing
+from . import requisition
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index 56f3e82c..c2e93632 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -33,13 +33,36 @@ class AccountMove(models.Model):
return res
def action_post(self):
+ if self._name != 'account.move':
+ return super(AccountMove, self).action_post()
+
+ # validation cant qty invoice greater than qty order
+ if self.move_type == 'out_invoice':
+ query = ["&",("name","=",self.invoice_origin),"|",("state","=","sale"),("state","=","done")]
+ sale_order = self.env['sale.order'].search(query, limit=1)
+ sum_qty_invoice = sum_qty_order = 0
+ for line in sale_order.order_line:
+ sum_qty_invoice += line.qty_invoiced
+ sum_qty_order += line.product_uom_qty
+ if sum_qty_invoice > sum_qty_order:
+ raise UserError('Error Qty Invoice akan lebih besar dari Qty Order jika lanjut Posting')
+ elif self.move_type == 'in_invoice':
+ query = ["&",("name","=",self.invoice_origin),"|",("state","=","purchase"),("state","=","done")]
+ purchase_order = self.env['purchase.order'].search(query, limit=1)
+ sum_qty_invoice = sum_qty_order = 0
+ for line in purchase_order.order_line:
+ sum_qty_invoice += line.qty_invoiced
+ sum_qty_order += line.product_uom_qty
+ if sum_qty_invoice > sum_qty_order:
+ raise UserError('Error Qty Invoice akan lebih besar dari Qty Order jika lanjut Posting')
+
res = super(AccountMove, self).action_post()
# if not self.env.user.is_accounting:
# raise UserError('Hanya Accounting yang bisa Posting')
- if self._name == 'account.move':
- for entry in self:
- for line in entry.line_ids:
- line.date_maturity = entry.date
+ # if self._name == 'account.move':
+ for entry in self:
+ for line in entry.line_ids:
+ line.date_maturity = entry.date
return res
def _compute_invoice_day_to_due(self):
diff --git a/indoteknik_custom/models/apache_solr.py b/indoteknik_custom/models/apache_solr.py
index a988a8d3..9f187d78 100644
--- a/indoteknik_custom/models/apache_solr.py
+++ b/indoteknik_custom/models/apache_solr.py
@@ -168,3 +168,12 @@ class ApacheSolr(models.Model):
_solr.add(documents)
end_time = time.time()
_logger.info("[SYNC_PRODUCT_TO_SOLR] Finish task add to solr. Time taken: {:.6f} seconds".format(end_time - start_time))
+
+ def _test_product_price(self, product_id=228178):
+ product = self.env['product.product'].search([('id', '=', product_id)], limit=1)
+ _logger.info('price incl tax: %s' % product._get_website_price_include_tax())
+ _logger.info('price excl tax: %s' % product._get_website_price_exclude_tax())
+ _logger.info('discount : %s' % product._get_website_disc(0))
+ _logger.info('price after discount : %s' % product._get_website_price_after_disc())
+ _logger.info('price excl after discount : %s' % product._get_website_price_after_disc_and_tax())
+ _logger.info('tax : %s' % product._get_website_tax())
diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py
index eea66b99..87319bf6 100644
--- a/indoteknik_custom/models/automatic_purchase.py
+++ b/indoteknik_custom/models/automatic_purchase.py
@@ -88,7 +88,7 @@ class AutomaticPurchase(models.Model):
if point.product_id.virtual_available > point.product_min_qty:
continue
qty_purchase = point.product_max_qty - point.product_id.virtual_available
- po_line = self.env['purchase.order.line'].search([('product_id', '=', point.product_id.id), ('order_id.state', '=', 'done')], limit=1)
+ po_line = self.env['purchase.order.line'].search([('product_id', '=', point.product_id.id), ('order_id.state', '=', 'done')], order='id desc', limit=1)
if self.vendor_id:
purchase_price = self.env['purchase.pricelist'].search([
diff --git a/indoteknik_custom/models/crm_lead.py b/indoteknik_custom/models/crm_lead.py
index e7af4b1c..b87315ff 100755
--- a/indoteknik_custom/models/crm_lead.py
+++ b/indoteknik_custom/models/crm_lead.py
@@ -81,7 +81,7 @@ class CrmLead(models.Model):
if not lead.partner_id:
continue
- if not lead.user_id or lead.user_id.id == 2:
+ if not lead.user_id or lead.user_id.id == 2 or lead.user_id.id == 25:
if lead.partner_id.parent_id.user_id:
salesperson_id = lead.partner_id.parent_id.user_id.id
elif lead.partner_id.user_id:
diff --git a/indoteknik_custom/models/manufacturing.py b/indoteknik_custom/models/manufacturing.py
index 15756e44..2455a117 100644
--- a/indoteknik_custom/models/manufacturing.py
+++ b/indoteknik_custom/models/manufacturing.py
@@ -25,8 +25,8 @@ class Manufacturing(models.Model):
return super(Manufacturing, self).button_mark_done()
for line in self.move_raw_ids:
- if line.quantity_done > 0 and line.quantity_done != self.product_uom_qty:
- raise UserError('Qty Consume per Line tidak sama dengan Qty to Produce')
+ # if line.quantity_done > 0 and line.quantity_done != self.product_uom_qty:
+ # raise UserError('Qty Consume per Line tidak sama dengan Qty to Produce')
if line.forecast_availability != line.product_uom_qty:
raise UserError('Qty Reserved belum sesuai dengan yang seharusnya')
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index 49235ec7..9c480f4c 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -1,5 +1,6 @@
from odoo import fields, models, api
from datetime import datetime, timedelta
+from odoo.exceptions import AccessError, UserError, ValidationError
import logging
_logger = logging.getLogger(__name__)
@@ -69,6 +70,10 @@ class ProductTemplate(models.Model):
rate += 2
product.virtual_rating = rate
+ def unlink(self):
+ if self._name == 'product.template':
+ raise UserError('Maaf anda tidak bisa delete product')
+
def update_new_product(self):
current_time = datetime.now()
delta_time = current_time - timedelta(days=30)
@@ -185,6 +190,8 @@ class ProductTemplate(models.Model):
product.product_rating = rate
product.last_calculate_rating = current_time
+
+
class ProductProduct(models.Model):
_inherit = "product.product"
@@ -223,3 +230,7 @@ class ProductProduct(models.Model):
for product in self:
stock_vendor = self.env['stock.vendor'].search([('product_variant_id', '=', product.id)], limit=1)
product.qty_stock_vendor = stock_vendor.quantity + product.qty_available
+
+ def unlink(self):
+ if self._name == 'product.product':
+ raise UserError('Maaf anda tidak bisa delete product') \ No newline at end of file
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index ea99e165..7e18bd8f 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -151,6 +151,10 @@ class PurchaseOrder(models.Model):
self.order_line.unlink()
for order_line in self.sale_order_id.order_line:
if order_line.product_id.id and order_line.product_id.id not in products_exception:
+ # qty_available = self.env['stock.quant'].search([
+ # ('product_id', '=', order_line.product_id.id),
+ # ('location_id', '=', "BU/Stock")
+ # ], limit=1)
qty_available = order_line.product_id.virtual_available
suggest = 'harus beli'
if order_line.product_id.virtual_available > order_line.product_qty:
diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py
new file mode 100644
index 00000000..5bb3272e
--- /dev/null
+++ b/indoteknik_custom/models/requisition.py
@@ -0,0 +1,179 @@
+from odoo import models, fields, api
+from odoo.exceptions import UserError
+from datetime import datetime
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class Requisition(models.Model):
+ _name = 'requisition'
+ _order = 'id desc'
+
+ number = fields.Char(string='Document No', index=True, copy=False, readonly=True)
+ date_doc = fields.Date(string='Date', help='isi tanggal hari ini')
+ description = fields.Char(string='Description', help='bebas isinya apa aja')
+ requisition_lines = fields.One2many('requisition.line', 'requisition_id', string='Lines', auto_join=True)
+ notification = fields.Char(string='Notification')
+ is_po = fields.Boolean(string='Is PO')
+ requisition_match = fields.One2many('requisition.purchase.match', 'requisition_id', string='Matches', auto_join=True)
+ sale_order_id = fields.Many2one('sale.order', string='SO', help='harus diisi nomor SO yang ingin digenerate',
+ domain="[('state', '=', 'sale')]")
+
+ @api.model
+ def create(self, vals):
+ vals['number'] = self.env['ir.sequence'].next_by_code('requisition') or '0'
+ result = super(Requisition, self).create(vals)
+ return result
+
+ def create_requisition_from_sales(self):
+ if self.requisition_lines:
+ raise UserError('Sudah digenerate sebelumnya, hapus line terlebih dahulu')
+ if not self.sale_order_id:
+ raise UserError('Sale Order harus diisi')
+ if self.is_po:
+ raise UserError('Sudah jadi PO, tidak bisa di create ulang PO nya')
+
+ # old_requisition = self.env['requisition'].search([('sale_order_id', '=', self.sale_order_id.id)], limit=1)
+ # if old_requisition:
+ # raise UserError('Sudah pernah jadi Requisition')
+
+ count = 0
+ for order_line in self.sale_order_id.order_line:
+ # get purchase price altama, if nothing, then get other cheaper, if nothing then last po
+ purchase_price = 0
+ vendor_id = 0
+
+ purchase_pricelist = self.env['purchase.pricelist'].search([
+ ('product_id.id', '=', order_line.product_id.id),
+ ('vendor_id.id', '=', 5571)
+ ], order='product_price asc', limit=1)
+ purchase_price = purchase_pricelist.product_price
+ vendor_id = purchase_pricelist.vendor_id.id
+ source = 'PriceList'
+
+ if not purchase_price or purchase_price <= 0:
+ purchase_pricelist = self.env['purchase.pricelist'].search([('product_id', '=', order_line.product_id.id)], order='product_price asc', limit=1)
+ purchase_price = purchase_pricelist.product_price
+ vendor_id = purchase_pricelist.vendor_id.id
+ source = 'PriceList'
+
+ if not purchase_price or purchase_price <= 0:
+ last_po_line = self.env['purchase.order.line'].search([('product_id', '=', order_line.product_id.id), ('order_id.state', '=', 'done')], order='id desc', limit=1)
+ purchase_price = last_po_line.price_unit
+ vendor_id = last_po_line.order_id.partner_id.id
+ source = 'LastPO'
+
+ if not purchase_price or purchase_price <= 0:
+ purchase_price = 0
+ vendor_id = 5571
+ source = 'Nothing'
+
+ self.env['requisition.line'].create([{
+ 'requisition_id': self.id,
+ 'partner_id': vendor_id,
+ 'brand_id': order_line.product_id.product_tmpl_id.x_manufacture.id,
+ 'product_id': order_line.product_id.id,
+ 'qty_purchase': order_line.product_uom_qty,
+ 'tax_id': order_line.purchase_tax_id.id,
+ 'price_unit': purchase_price,
+ 'subtotal': purchase_price * order_line.product_uom_qty,
+ 'source': source
+ }])
+ count+=1
+ _logger.info('Create Requisition %s' % order_line.product_id.name)
+ self.notification = "Requisition Created %s lines" % count
+
+ def create_po_from_requisition(self):
+ if not self.requisition_lines:
+ raise UserError('Tidak ada Lines, belum bisa create PO')
+ if self.is_po:
+ raise UserError('Sudah pernah di create PO')
+ current_time = datetime.now()
+ vendor_ids = self.env['requisition.line'].read_group([('requisition_id', '=', self.id), ('partner_id', '!=', False)], fields=['partner_id'], groupby=['partner_id'])
+
+ counter_po_number = 0
+ for vendor in vendor_ids:
+ param_header = {
+ 'partner_id': vendor['partner_id'][0],
+ 'partner_ref': self.sale_order_id.name,
+ '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,
+ 'sale_order_id': self.sale_order_id.id
+ }
+ # new_po = self.env['purchase.order'].create([param_header])
+ products_vendors = self.env['requisition.line'].search([
+ ('requisition_id', '=', self.id),
+ ('partner_id', '=', vendor['partner_id'][0]),
+ ('qty_purchase', '>', 0)
+ ], order='brand_id')
+ count = brand_id = 0
+ new_po = ''
+ for product in products_vendors:
+ if not new_po or ((count == 200 or brand_id != product.brand_id.id) and vendor['partner_id'][0] == 5571):
+ count = 0
+ counter_po_number += 1
+ new_po = self.env['purchase.order'].create([param_header])
+ new_po.name = new_po.name + "/R/"+str(counter_po_number)
+ self.env['requisition.purchase.match'].create([{
+ 'requisition_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
+ param_line = {
+ 'order_id': new_po.id,
+ 'sequence': count,
+ 'product_id': product.product_id.id,
+ 'product_qty': product.qty_purchase,
+ 'product_uom_qty': product.qty_purchase,
+ 'price_unit': product.last_price,
+ 'taxes_id': product.tax_id
+ }
+ 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('Create PO Line %s' % product.product_id.name)
+ self.notification = self.notification + ' %s' % new_po.name
+ self.is_po = True
+
+class RequisitionLine(models.Model):
+ _name = 'requisition.line'
+ _description = 'Requisition Line'
+ _order = 'requisition_id, id'
+
+ requisition_id = fields.Many2one('requisition', string='Ref', required=True, ondelete='cascade', index=True, copy=False)
+ brand_id = fields.Many2one('x_manufactures', string='Brand')
+ product_id = fields.Many2one('product.product', string='Product')
+ partner_id = fields.Many2one('res.partner', string='Vendor')
+ qty_purchase = fields.Float(string='Qty Purchase')
+ price_unit = fields.Float(string='Price')
+ tax_id = fields.Many2one('account.tax', help='isi tax pembelian include atau exclude', domain="[('type_tax_use', '=', 'purchase')]")
+ subtotal = fields.Float(string='Subtotal')
+ last_price = fields.Float(string='Last Price')
+ last_order_id = fields.Many2one('purchase.order', string='Last Order')
+ last_orderline_id = fields.Many2one('purchase.order.line', string='Last Order Line')
+ is_po = fields.Boolean(String='Is PO')
+ current_po_id = fields.Many2one('purchase.order', string='Current')
+ current_po_line_id = fields.Many2one('purchase.order.line', string='Current Line')
+ source = fields.Char(string='Source', help='data harga diambil darimana')
+
+class RequisitionPurchaseMatch(models.Model):
+ _name = 'requisition.purchase.match'
+ _order = 'requisition_id, id'
+
+ requisition_id = fields.Many2one('requisition', string='Ref', required=True, ondelete='cascade', index=True, copy=False)
+ order_id = fields.Many2one('purchase.order', string='Purchase Order')
+ vendor = fields.Char(string='Vendor', compute='_compute_info_po')
+ total = fields.Float(string='Total', compute='_compute_info_po')
+
+ def _compute_info_po(self):
+ for match in self:
+ match.vendor = match.order_id.partner_id.name
+ match.total = match.order_id.amount_total
diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py
index eaf93717..77abaaf9 100644
--- a/indoteknik_custom/models/res_partner.py
+++ b/indoteknik_custom/models/res_partner.py
@@ -1,4 +1,5 @@
from odoo import models, fields
+from odoo.exceptions import UserError, ValidationError
class GroupPartner(models.Model):
_name = 'group.partner'
@@ -13,3 +14,9 @@ class ResPartner(models.Model):
custom_pricelist_id = fields.Many2one('product.pricelist', string='Price Matrix')
group_partner_id = fields.Many2one('group.partner', string='Group Partner')
+ def unlink(self):
+ if self._name == 'res.partner':
+ raise UserError('Maaf anda tidak bisa delete contact')
+
+
+
diff --git a/indoteknik_custom/models/sale_monitoring.py b/indoteknik_custom/models/sale_monitoring.py
index a3265a8b..e79ff9ab 100755
--- a/indoteknik_custom/models/sale_monitoring.py
+++ b/indoteknik_custom/models/sale_monitoring.py
@@ -20,6 +20,7 @@ class SaleMonitoring(models.Model):
date_order = fields.Datetime(string="Date Order")
status = fields.Char(string="Status")
po_number = fields.Char(string="PO Number")
+ qty_reserved = fields.Integer(string="Qty Reserved")
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
@@ -36,14 +37,14 @@ class SaleMonitoring(models.Model):
SUM(smd.qty_po_received) AS qty_po_received,
SUM(smd.qty_so_delivered) AS qty_so_delivered,
SUM(smd.qty_so_invoiced) AS qty_so_invoiced,
+ sum(smd.qty_reserved) as qty_reserved,
CASE
- WHEN SUM(qty_po) < SUM(qty_so) AND SUM(qty_po) <= 0 THEN 'Belum PO sama sekali'
- WHEN SUM(qty_po) < SUM(qty_so) THEN 'Belum PO full'
- WHEN SUM(qty_po_received) < SUM(qty_so) AND SUM(qty_po_received) <= 0 THEN 'Belum Received sama sekali'
- WHEN SUM(qty_po_received) < SUM(qty_so) THEN 'Belum Received full'
- WHEN SUM(qty_to_delivered) = SUM(qty_so) THEN 'SIAP KIRIM'
- WHEN SUM(qty_to_delivered) < SUM(qty_so) and SUM(qty_to_delivered) > 0 THEN 'KIRIM SISANYA'
- ELSE 'Belum Invoiced'
+ when sum(qty_so_invoiced) = sum(qty_so) then 'Invoiced'
+ when sum(qty_so_delivered) = sum(qty_so) then 'Delivered'
+ when sum(qty_reserved) >= sum(qty_so) then 'Siap kirim'
+ when sum(qty_po) + sum(qty_reserved) - sum(qty_po_received) < sum(qty_so) then 'Belum/Kurang PO'
+ when sum(qty_po_received) = 0 then 'Belum terima'
+ when sum(qty_po_received) < sum(qty_po) then 'Terima sebagian'
END AS status,
get_po_number(smd.sale_order_id) as po_number
FROM sale_monitoring_detail smd
diff --git a/indoteknik_custom/models/sale_monitoring_detail.py b/indoteknik_custom/models/sale_monitoring_detail.py
index a578f67b..2bcda50c 100755
--- a/indoteknik_custom/models/sale_monitoring_detail.py
+++ b/indoteknik_custom/models/sale_monitoring_detail.py
@@ -21,6 +21,7 @@ class SaleMonitoringDetail(models.Model):
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")
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
@@ -29,14 +30,12 @@ class SaleMonitoringDetail(models.Model):
SELECT
*,
CASE
- WHEN qty_so = qty_so_invoiced then 'Done'
- WHEN qty_po < qty_so AND qty_po <= 0 THEN 'Belum PO sama sekali'
- WHEN qty_po < qty_so THEN 'Belum PO full'
- WHEN qty_po_received < qty_so and qty_po_received <= 0 THEN 'Belum Received sama sekali'
- WHEN qty_po_received < qty_so THEN 'Belum Received full'
- WHEN qty_to_delivered = qty_so THEN 'SIAP KIRIM'
- WHEN qty_to_delivered < qty_so and qty_to_delivered > 0 THEN 'KIRIM SISANYA'
- ELSE 'Belum Invoiced'
+ when qty_so_invoiced = qty_so then 'Invoiced'
+ 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'
END AS status
FROM
(
@@ -52,7 +51,8 @@ class SaleMonitoringDetail(models.Model):
sol.qty_invoiced AS qty_so_invoiced,
so.date_order AS date_order,
get_qty_po(so.id, sol.product_id) AS qty_po,
- get_qty_received(so.id, sol.product_id) AS qty_po_received
+ get_qty_received(so.id, sol.product_id) AS qty_po_received,
+ get_qty_reserved(so.id, sol.product_id) as qty_reserved
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
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 85d7e595..cbc6a60a 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -5,6 +5,9 @@ import logging
import warnings
import random
import string
+import requests
+import math
+import json
_logger = logging.getLogger(__name__)
@@ -50,7 +53,7 @@ class SaleOrder(models.Model):
('terproses', 'Terproses'),
('sebagian', 'Sebagian Diproses'),
('menunggu', 'Menunggu Diproses'),
- ])
+ ], copy=False)
partner_purchase_order_name = fields.Char(string='Nama PO Customer', copy=False, help="Nama purchase order customer, diisi oleh customer melalui website.", tracking=3)
partner_purchase_order_description = fields.Text(string='Keterangan PO Customer', copy=False, help="Keterangan purchase order customer, diisi oleh customer melalui website.", tracking=3)
partner_purchase_order_file = fields.Binary(string='File PO Customer', copy=False, help="File purchase order customer, diisi oleh customer melalui website.")
@@ -74,6 +77,36 @@ class SaleOrder(models.Model):
notification = fields.Char(string='Notification', help='Dapat membantu error dari approval')
delivery_service_type = fields.Char(string='Delivery Service Type', help='data dari rajaongkir')
grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', compute='_compute_grand_total')
+ payment_link_midtrans = fields.Char(string='Payment Link', help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri')
+
+ def generate_payment_link_midtrans_sales_order(self):
+ # midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox
+ # midtrans_auth = 'Basic U0ItTWlkLXNlcnZlci1uLVY3ZDJjMlpCMFNWRUQyOU95Q1dWWXA6' # dev - sandbox
+ midtrans_url = 'https://app.midtrans.com/snap/v1/transactions' # production
+ midtrans_auth = 'Basic TWlkLXNlcnZlci1SbGMxZ2gzWGpSVW5scl9JblZzTV9OTnU6' # production
+ so_number = self.name
+ so_number = so_number.replace('/', '-')
+ so_grandtotal = math.floor(self.grand_total)
+ headers = {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': midtrans_auth,
+ }
+
+ json_data = {
+ 'transaction_details': {
+ 'order_id': so_number,
+ 'gross_amount': so_grandtotal,
+ },
+ 'credit_card': {
+ 'secure': True,
+ },
+ }
+
+ response = requests.post(midtrans_url, headers=headers, json=json_data).json()
+ lookup_json = json.dumps(response, indent=4, sort_keys=True)
+ redirect_url = json.loads(lookup_json)['redirect_url']
+ self.payment_link_midtrans = str(redirect_url)
@api.model
def _generate_so_access_token(self, limit=50):
@@ -116,6 +149,33 @@ class SaleOrder(models.Model):
sale.so_status = 'terproses'
_logger.info('Calculate SO Status %s' % sale.id)
+ def _calculate_all_so_status(self, limit=500):
+ so_state = ['sale']
+ sales = self.env['sale.order'].search([
+ ('state', 'in', so_state),
+ # ('so_status', '!=', 'terproses'),
+ ], order='id desc', limit=limit)
+ for sale in sales:
+ sum_qty_ship = sum_qty_so = 0
+ have_outstanding_pick = False
+
+ for pick in sale.picking_ids:
+ if pick.state == 'draft' or pick.state == 'assigned' or pick.state == 'confirmed' or pick.state == 'waiting':
+ have_outstanding_pick = True
+
+ for so_line in sale.order_line:
+ sum_qty_so += so_line.product_uom_qty
+ sum_qty_ship += so_line.qty_delivered
+
+ if have_outstanding_pick:
+ if sum_qty_so > sum_qty_ship > 0:
+ sale.so_status = 'sebagian'
+ else:
+ sale.so_status = 'menunggu'
+ else:
+ sale.so_status = 'terproses'
+ _logger.info('Calculate All SO Status %s' % sale.id)
+
def calculate_so_status(self):
so_state = ['sale']
sales = self.env['sale.order'].search([
@@ -168,7 +228,7 @@ class SaleOrder(models.Model):
order.have_outstanding_invoice = False
def _have_outstanding_picking(self):
- picking_state = ['done', 'confirmed', 'draft', 'cancel']
+ picking_state = ['done', 'confirmed', 'draft']
for order in self:
if not order.picking_ids:
order.have_outstanding_picking = False
@@ -251,6 +311,9 @@ class SaleOrder(models.Model):
for line in order.order_line:
if not line.product_id or line.product_id.type == 'service':
continue
+ # must add product can sell validation
+ if not line.product_id.product_tmpl_id.sale_ok:
+ raise UserError('Product %s belum bisa dijual, harap hubungi finance' % line.product_id.display_name)
if line.product_id.id == 232383:
raise UserError(_('Tidak bisa Confirm menggunakan Produk Sementara'))
if not line.vendor_id or not line.purchase_price:
@@ -266,12 +329,15 @@ class SaleOrder(models.Model):
def action_cancel(self):
# TODO stephan prevent cancel if have invoice, do, and po
+ if self._name != 'sale.order':
+ return super(SaleOrder, self).action_cancel()
+
if self.have_outstanding_invoice:
raise UserError("Invoice harus di Cancel dahulu")
- # elif self.have_outstanding_picking:
- # raise UserError("DO harus di Cancel dahulu")
- # elif self.have_outstanding_po:
- # raise UserError("PO harus di Cancel dahulu")
+ elif self.have_outstanding_picking:
+ raise UserError("DO harus di Cancel dahulu")
+ elif self.have_outstanding_po:
+ raise UserError("PO harus di Cancel dahulu")
self.approval_status = False
return super(SaleOrder, self).action_cancel()
@@ -289,6 +355,9 @@ class SaleOrder(models.Model):
for line in order.order_line:
if not line.product_id or line.product_id.type == 'service':
continue
+ # must add product can sell validation
+ if not line.product_id.product_tmpl_id.sale_ok:
+ raise UserError('Product %s belum bisa dijual, harap hubungi finance' % line.product_id.display_name)
if line.product_id.id == 232383:
raise UserError(_('Tidak bisa Confirm menggunakan Produk Sementara'))
if not line.vendor_id or not line.purchase_price or not line.purchase_tax_id:
@@ -365,7 +434,10 @@ class SaleOrder(models.Model):
def _compute_grand_total(self):
for order in self:
- order.grand_total = order.delivery_amt + order.amount_total
+ if order.shipping_cost_covered == 'customer':
+ order.grand_total = order.delivery_amt + order.amount_total
+ else:
+ order.grand_total = order.amount_total
class SaleOrderLine(models.Model):
@@ -422,15 +494,15 @@ class SaleOrderLine(models.Model):
if not self.product_id or self.product_id.type == 'service':
return
elif self.product_id.categ_id.id == 34: # finish good / manufacturing only
- print('a')
- bom = self.env['mrp.bom'].search(
- [('product_tmpl_id', '=', self.product_id.product_tmpl_id.id)]
- , limit=1)
- cost = 0
- for line in bom.bom_line_ids:
- purchase_price = self.env['purchase.pricelist'].search(
- [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', line.product_id.id)], limit=1)
- cost += purchase_price.product_price
+ # bom = self.env['mrp.bom'].search(
+ # [('product_tmpl_id', '=', self.product_id.product_tmpl_id.id)]
+ # , limit=1)
+ # cost = 0
+ # for line in bom.bom_line_ids:
+ # purchase_price = self.env['purchase.pricelist'].search(
+ # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', line.product_id.id)], limit=1)
+ # cost += purchase_price.product_price
+ cost = self.product_id.standard_price
self.purchase_price = cost
else:
purchase_price = self.env['purchase.pricelist'].search(
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index e63370f5..c8424121 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -59,6 +59,10 @@ class StockPicking(models.Model):
('approved', 'Approved'),
], string='Approval Return Status', readonly=True, copy=False, index=True, tracking=3, help="Approval Status untuk Return")
date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ, tidak berpengaruh ke Accounting")
+ note_logistic = fields.Selection([
+ ('hold', 'Hold by Sales'),
+ ('not_paid', 'Customer belum bayar')
+ ], string='Note', help='jika field ini diisi maka tidak akan dihitung ke lead time')
def action_create_invoice_from_mr(self):
"""Create the invoice associated to the PO.
@@ -248,6 +252,9 @@ class StockPicking(models.Model):
return
def button_validate(self):
+ if self._name != 'stock.picking':
+ return super(StockPicking, self).button_validate()
+
if not self.picking_code:
self.picking_code = self.env['ir.sequence'].next_by_code('stock.picking.code') or '0'
@@ -256,6 +263,11 @@ class StockPicking(models.Model):
if self.is_internal_use and not self.env.user.is_accounting:
raise UserError("Harus di Approve oleh Accounting")
+
+ if self.group_id.sale_id:
+ if self.group_id.sale_id.payment_link_midtrans:
+ if self.group_id.sale_id.payment_status != 'settlement':
+ raise UserError('Uang belum masuk (settlement), mohon konfirmasi ke sales atau finance')
if self.is_internal_use:
stock_move_lines = self.env['stock.move.line'].search([
diff --git a/indoteknik_custom/models/typesense_client.py b/indoteknik_custom/models/typesense_client.py
new file mode 100644
index 00000000..fccd04e3
--- /dev/null
+++ b/indoteknik_custom/models/typesense_client.py
@@ -0,0 +1,150 @@
+from odoo import models
+import typesense
+import logging
+import time
+
+
+_logger = logging.getLogger(__name__)
+_typesense = typesense.Client({
+ 'nodes': [{
+ 'host': 'localhost',
+ 'port': '9090',
+ 'protocol': 'http'
+ }],
+ 'api_key': 'WKWKdwakdjopwakfoij21321fkdmvaskamd'
+})
+
+class Typesense(models.Model):
+ _name = 'typesense.client'
+
+ def _check_collection(self, name):
+ collections = _typesense.collections.retrieve()
+ for collection in collections:
+ if collection['name'] == name:
+ return True
+ return False
+
+ def _init_collection(self, name):
+ is_exist = self._check_collection(name)
+ if is_exist:
+ return False
+
+ schema = {
+ "name": name,
+ "fields": [
+ {"name": ".*", "type": "auto" },
+ {"name": ".*_facet", "type": "auto", "facet": True }
+ ]
+ }
+ _typesense.collections.create(schema)
+ return True
+
+ def _indexing_product(self, limit=500):
+ start_time = time.time()
+ self._init_collection('products')
+
+ query = ["&", "&", ("type", "=", "product"), ("active", "=", True), "|", ("solr_flag", "=", 0), ("solr_flag", "=", 2)]
+ product_templates = self.env['product.template'].search(query, limit=limit)
+
+ counter = 0
+ documents = []
+ for product_template in product_templates:
+ counter += 1
+ template_time = time.time()
+ document = self._map_product_document(product_template)
+ documents.append(document)
+ product_template.solr_flag = 1
+ _logger.info('[SYNC_PRODUCT_TO_TYPESENSE] {}/{} {:.6f}'.format(counter, limit, time.time() - template_time))
+ _logger.info('[SYNC_PRODUCT_TO_TYPESENSE] Success add to typesense product %s' % product_template.id)
+
+ _typesense.collections['products'].documents.import_(documents, {'action': 'upsert'})
+ end_time = time.time()
+ _logger.info("[SYNC_PRODUCT_TO_SOLR] Finish task add to solr. Time taken: {:.6f} seconds".format(end_time - start_time))
+
+ def _map_product_document(self, product_template):
+ price_excl_after_disc = price_excl = discount = tax = 0
+ variants_name = variants_code = ''
+ flashsale_data = tier1 = tier2 = tier3 = {}
+ if product_template.product_variant_count > 1:
+ for variant in product_template.product_variant_ids:
+ if price_excl_after_disc == 0 or variant._get_website_price_after_disc_and_tax() < price_excl_after_disc:
+ price_excl = variant._get_website_price_exclude_tax()
+ price_excl_after_disc = variant._get_website_price_after_disc_and_tax()
+ discount = variant._get_website_disc(0)
+ tax = variant._get_website_tax()
+ flashsale_data = variant._get_flashsale_price()
+ # add price tiering for base price, discount, and price after discount (tier 1 - 3)
+ tier1 = variant._get_pricelist_tier1()
+ tier2 = variant._get_pricelist_tier2()
+ tier3 = variant._get_pricelist_tier3()
+ else:
+ price_excl_after_disc = price_excl_after_disc
+ price_excl = price_excl
+ discount = discount
+ tax = tax
+ flashsale_data = flashsale_data
+ tier1 = tier1
+ tier2 = tier2
+ tier3 = tier3
+ variants_name += variant.display_name or ''+', '
+ variants_code += variant.default_code or ''+', '
+ else:
+ variants_name = product_template.display_name
+ price_excl = product_template.product_variant_id._get_website_price_exclude_tax()
+ discount = product_template.product_variant_id._get_website_disc(0)
+ price_excl_after_disc = product_template.product_variant_id._get_website_price_after_disc_and_tax()
+ tax = product_template.product_variant_id._get_website_tax()
+ flashsale_data = product_template.product_variant_id._get_flashsale_price()
+ tier1 = product_template.product_variant_id._get_pricelist_tier1()
+ tier2 = product_template.product_variant_id._get_pricelist_tier2()
+ tier3 = product_template.product_variant_id._get_pricelist_tier3()
+
+ category_id = ''
+ category_name = ''
+ for category in product_template.public_categ_ids:
+ category_id = category.id
+ category_name = category.name
+
+ document = {
+ 'id': str(product_template.id),
+ 'display_name': product_template.display_name,
+ 'name': product_template.name,
+ 'default_code': product_template.default_code or '',
+ 'product_rating': product_template.virtual_rating,
+ 'product_id': product_template.id,
+ 'image': self.env['ir.attachment'].api_image('product.template', 'image_512', product_template.id),
+ 'price': price_excl,
+ 'discount': discount,
+ 'price_discount': price_excl_after_disc,
+ 'tax': tax,
+ 'variant_total': product_template.product_variant_count,
+ 'stock_total': product_template.qty_stock_vendor,
+ 'weight': product_template.weight,
+ 'manufacture_id': product_template.x_manufacture.id or 0,
+ 'manufacture_name': product_template.x_manufacture.x_name or '',
+ 'manufacture_name': product_template.x_manufacture.x_name or '',
+ 'image_promotion_1': self.env['ir.attachment'].api_image('x_manufactures', 'image_promotion_1', product_template.x_manufacture.id),
+ 'image_promotion_2': self.env['ir.attachment'].api_image('x_manufactures', 'image_promotion_2', product_template.x_manufacture.id),
+ 'category_id': category_id or 0,
+ 'category_name': category_name or '',
+ 'category_name': category_name or '',
+ 'variants_name_t': variants_name,
+ 'variants_code_t': variants_code,
+ 'search_rank': product_template.search_rank,
+ 'search_rank_weekly': product_template.search_rank_weekly,
+ 'flashsale_id': flashsale_data['flashsale_id'] or 0,
+ 'flashsale_name': flashsale_data['flashsale_name'] or '',
+ 'flashsale_base_price': flashsale_data['flashsale_base_price'] or 0,
+ 'flashsale_discount': flashsale_data['flashsale_discount'] or 0,
+ 'flashsale_price': flashsale_data['flashsale_price'] or 0,
+ 'discount_tier1': tier1['discount_tier1'] or 0,
+ 'price_tier1': tier1['price_tier1'] or 0,
+ 'discount_tier2': tier2['discount_tier2'] or 0,
+ 'price_tier2': tier2['price_tier2'] or 0,
+ 'discount_tier3': tier3['discount_tier3'] or 0,
+ 'price_tier3': tier3['price_tier3'] or 0
+ }
+
+ return document
+
+
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index f782046d..3d23529f 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -49,7 +49,13 @@ access_group_partner,access.group.partner,model_group_partner,,1,1,1,1
access_procurement_monitoring_detail,access.procurement.monitoring.detail,model_procurement_monitoring_detail,,1,1,1,1
access_rajaongkir_kurir,access.rajaongkir.kurir,model_rajaongkir_kurir,,1,1,1,1
access_brand_vendor,access.brand.vendor,model_brand_vendor,,1,1,1,1
+<<<<<<< HEAD
access_promotion_program,access.promotion.program,model_promotion_program,,1,1,1,1
access_promotion_program_line,access.promotion.program.line,model_promotion_program_line,,1,1,1,1
access_promotion_program_free_item,access.promotion.program.free_item,model_promotion_program_free_item,,1,1,1,1
-access_promotion_program_keyword,access.promotion.program.keyword,model_promotion_program_keyword,,1,1,1,1 \ No newline at end of file
+access_promotion_program_keyword,access.promotion.program.keyword,model_promotion_program_keyword,,1,1,1,1
+=======
+access_requisition,access.requisition,model_requisition,,1,1,1,1
+access_requisition_line,access.requisition.line,model_requisition_line,,1,1,1,1
+access_requisition_purchase_match,access.requisition.purchase.match,model_requisition_purchase_match,,1,1,1,1
+>>>>>>> 24649f8e939484759ef34e5e68f251d951f63c02
diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml
index 6a6f474d..9af85408 100644
--- a/indoteknik_custom/views/ir_sequence.xml
+++ b/indoteknik_custom/views/ir_sequence.xml
@@ -30,5 +30,15 @@
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
+
+ <record id="sequence_requisition" model="ir.sequence">
+ <field name="name">Requisition</field>
+ <field name="code">requisition</field>
+ <field name="active">TRUE</field>
+ <field name="prefix">RPO/%(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/landedcost.xml b/indoteknik_custom/views/landedcost.xml
new file mode 100644
index 00000000..4736941a
--- /dev/null
+++ b/indoteknik_custom/views/landedcost.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <data>
+ <record id="landed_cost_form_inherit" model="ir.ui.view">
+ <field name="name">Landed Cost</field>
+ <field name="model">stock.landed.cost</field>
+ <field name="inherit_id" ref="stock_landed_costs.view_stock_landed_cost_form"/>
+ <field name="arch" type="xml">
+ <field name="vendor_bill_id" position="replace">
+ <field name="vendor_bill_id" required="1"/>
+ </field>
+ </field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/requisition.xml b/indoteknik_custom/views/requisition.xml
new file mode 100644
index 00000000..6e61ce1e
--- /dev/null
+++ b/indoteknik_custom/views/requisition.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <record id="requisition_tree" model="ir.ui.view">
+ <field name="name">requisition.tree</field>
+ <field name="model">requisition</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="date_doc"/>
+ <field name="number"/>
+ <field name="description"/>
+ <field name="notification" readonly="1"/>
+ <field name="is_po" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="requisition_line_tree" model="ir.ui.view">
+ <field name="name">requisition.line.tree</field>
+ <field name="model">requisition.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="brand_id"/>
+ <field name="product_id"/>
+ <field name="partner_id"/>
+ <field name="qty_purchase"/>
+ <field name="price_unit"/>
+ <field name="tax_id"/>
+ <field name="subtotal"/>
+ <field name="source"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="requisition_purchase_match_tree" model="ir.ui.view">
+ <field name="name">requisition.purchase.match.tree</field>
+ <field name="model">requisition.purchase.match</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="order_id" readonly="1"/>
+ <field name="vendor" readonly="1"/>
+ <field name="total" readonly="1"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="requisition_form" model="ir.ui.view">
+ <field name="name">requisition.form</field>
+ <field name="model">requisition</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet string="Requisition">
+ <div class="oe_button_box" name="button_box"/>
+ <group>
+ <group>
+ <field name="number"/>
+ <field name="date_doc"/>
+ <field name="sale_order_id"/>
+ <field name="description"/>
+ <field name="notification" readonly="1"/>
+ </group>
+ <group>
+ <div>
+ <button name="create_requisition_from_sales"
+ string="Create Line"
+ type="object"
+ class="mr-2 oe_highlight"
+ />
+ <button name="create_po_from_requisition"
+ string="Create PO"
+ type="object"
+ class="mr-2 oe_highlight"
+ />
+ </div>
+ </group>
+ </group>
+ <notebook>
+ <page string="Lines">
+ <field name="requisition_lines"/>
+ </page>
+ <page string="Matches">
+ <field name="requisition_match"/>
+ </page>
+ </notebook>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="requisition_action" model="ir.actions.act_window">
+ <field name="name">Requisition</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">requisition</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem id="menu_requisition"
+ name="Requisition"
+ action="requisition_action"
+ parent="menu_monitoring_in_purchase"
+ sequence="300"/>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/sale_monitoring.xml b/indoteknik_custom/views/sale_monitoring.xml
index b3b186e9..956e4a24 100755
--- a/indoteknik_custom/views/sale_monitoring.xml
+++ b/indoteknik_custom/views/sale_monitoring.xml
@@ -13,16 +13,17 @@
<field name="partner_id"/>
<field name="user_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 PO sama sekali' or status == 'Belum PO full'"
- decoration-warning="status == 'Belum Received sama sekali' or status == 'Belum Received full'"
- decoration-success="status == 'SIAP KIRIM' or status == 'KIRIM SISANYA'"
- decoration-info="status == 'Belum Invoiced'"
+ 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'"
/>
</tree>
</field>
@@ -41,15 +42,16 @@
<field name="user_id"/>
<field name="status"
widget="badge"
- decoration-danger="status == 'Belum PO sama sekali' or status == 'Belum PO full'"
- decoration-warning="status == 'Belum Received sama sekali' or status == 'Belum Received full'"
- decoration-success="status == 'SIAP KIRIM' or status == 'KIRIM SISANYA'"
- decoration-info="status == 'Belum Invoiced'"
+ 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="po_number"/>
</group>
<group>
<field name="qty_so"/>
+ <field name="qty_reserved"/>
<field name="qty_po"/>
<field name="qty_po_received"/>
<field name="qty_so_delivered"/>
diff --git a/indoteknik_custom/views/sale_monitoring_detail.xml b/indoteknik_custom/views/sale_monitoring_detail.xml
index b9602abd..736920b3 100755
--- a/indoteknik_custom/views/sale_monitoring_detail.xml
+++ b/indoteknik_custom/views/sale_monitoring_detail.xml
@@ -11,16 +11,17 @@
<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 PO sama sekali' or status == 'Belum PO full'"
- decoration-warning="status == 'Belum Received sama sekali' or status == 'Belum Received full'"
- decoration-success="status == 'SIAP KIRIM' or status == 'KIRIM SISANYA'"
- decoration-info="status == 'Belum Invoiced' or status == 'Done'"
+ 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'"
/>
</tree>
</field>
@@ -40,14 +41,15 @@
<field name="product_id"/>
<field name="status"
widget="badge"
- decoration-danger="status == 'Belum PO sama sekali' or status == 'Belum PO full'"
- decoration-warning="status == 'Belum Received sama sekali' or status == 'Belum Received full'"
- decoration-success="status == 'SIAP KIRIM' or status == 'KIRIM SISANYA'"
- decoration-info="status == 'Belum Invoiced'"
+ 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'"
/>
</group>
<group>
<field name="qty_so"/>
+ <field name="qty_reserved"/>
<field name="qty_po"/>
<field name="qty_po_received"/>
<field name="qty_so_delivered"/>
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index c66201a9..ea13c954 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -19,8 +19,8 @@
type="action" attrs="{'invisible': [('approval_status', '!=', 'approved')]}"/>
</button>
<field name="payment_term_id" position="after">
- <field name="shipping_cost_covered"/>
- <field name="shipping_paid_by"/>
+ <field name="shipping_cost_covered" required="1"/>
+ <field name="shipping_paid_by" required="1"/>
<field name="delivery_amt"/>
<field name="fee_third_party"/>
<field name="total_percent_margin"/>
@@ -82,6 +82,11 @@
<field name="partner_purchase_order_file" readonly="True"/>
</group>
<group>
+ <button name="generate_payment_link_midtrans_sales_order"
+ string="Create Payment Link"
+ type="object"
+ />
+ <field name="payment_link_midtrans" readonly="True" widget="url"/>
<field name="gross_amount" readonly="True"/>
<field name="payment_type" readonly="True"/>
<field name="payment_status" readonly="True"/>
diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml
index e4e9f722..4bbdc7b8 100644
--- a/indoteknik_custom/views/stock_picking.xml
+++ b/indoteknik_custom/views/stock_picking.xml
@@ -7,10 +7,11 @@
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<field name="json_popover" position="after">
- <field name="date_done"/>
- <field name="date_doc_kirim"/>
- <field name="driver_departure_date"/>
- <field name="driver_arrival_date"/>
+ <field name="date_done" optional="hide"/>
+ <field name="date_doc_kirim" optional="hide"/>
+ <field name="driver_departure_date" optional="hide"/>
+ <field name="driver_arrival_date" optional="hide"/>
+ <field name="note_logistic" optional="hide"/>
</field>
</field>
</record>
@@ -80,6 +81,7 @@
<page string="Delivery" name="delivery_order">
<group>
<group>
+ <field name="note_logistic"/>
<field name="driver_departure_date"/>
<field name="driver_arrival_date"/>
<field name="delivery_tracking_no"/>