summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2024-02-07 10:45:51 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2024-02-07 10:45:51 +0700
commitf6be42d37a363b86f4a9ec71ccb38c78cbe2d887 (patch)
tree9c297e776f67e35671012e94d3c64007bd5090d7
parentedb3c1c80931078d40a8f56149aeca9efdcdc07d (diff)
parent29a9ec94f1ad131f398cf119a03a7b927a4c6cba (diff)
Merge branch 'production' into purchasing-job
# Conflicts: # indoteknik_custom/__manifest__.py # indoteknik_custom/models/__init__.py # indoteknik_custom/models/automatic_purchase.py # indoteknik_custom/models/purchase_order_line.py # indoteknik_custom/security/ir.model.access.csv # indoteknik_custom/views/automatic_purchase.xml # indoteknik_custom/views/purchase_order.xml
-rw-r--r--indoteknik_api/controllers/api_v1/cart.py21
-rw-r--r--indoteknik_api/controllers/api_v1/promotion.py22
-rw-r--r--indoteknik_api/controllers/controller.py79
-rw-r--r--indoteknik_api/static/src/fonts/Inter.ttfbin0 -> 805360 bytes
-rw-r--r--indoteknik_api/static/src/images/logo-indoteknik-footer.pngbin0 -> 15294 bytes
-rw-r--r--indoteknik_api/static/src/images/logo-indoteknik-gray.pngbin0 -> 29514 bytes
-rw-r--r--indoteknik_api/static/src/images/logo-indoteknik.pngbin0 -> 33475 bytes
-rwxr-xr-xindoteknik_custom/__manifest__.py6
-rwxr-xr-xindoteknik_custom/models/__init__.py11
-rw-r--r--indoteknik_custom/models/account_move.py32
-rw-r--r--indoteknik_custom/models/automatic_purchase.py11
-rw-r--r--indoteknik_custom/models/base_import_import.py39
-rw-r--r--indoteknik_custom/models/commision.py17
-rwxr-xr-xindoteknik_custom/models/crm_lead.py17
-rw-r--r--indoteknik_custom/models/invoice_reklas.py1
-rw-r--r--indoteknik_custom/models/invoice_reklas_penjualan.py79
-rw-r--r--indoteknik_custom/models/logbook_sj.py117
-rw-r--r--indoteknik_custom/models/po_multi_cancel.py22
-rwxr-xr-xindoteknik_custom/models/product_template.py152
-rw-r--r--indoteknik_custom/models/promotion/promotion_program.py1
-rw-r--r--indoteknik_custom/models/promotion/promotion_program_line.py11
-rwxr-xr-xindoteknik_custom/models/purchase_order.py102
-rwxr-xr-xindoteknik_custom/models/purchase_order_line.py67
-rw-r--r--indoteknik_custom/models/purchase_order_multi_confirm.py22
-rw-r--r--indoteknik_custom/models/purchase_order_multi_update.py22
-rwxr-xr-xindoteknik_custom/models/purchase_pricelist.py45
-rw-r--r--indoteknik_custom/models/report_logbook_sj.py34
-rw-r--r--indoteknik_custom/models/requisition.py303
-rw-r--r--indoteknik_custom/models/res_partner.py1
-rw-r--r--indoteknik_custom/models/sale_advance_payment_inv.py1
-rwxr-xr-xindoteknik_custom/models/sale_monitoring_detail.py14
-rwxr-xr-xindoteknik_custom/models/sale_order.py58
-rw-r--r--indoteknik_custom/models/sale_order_line.py36
-rw-r--r--indoteknik_custom/models/solr/__init__.py4
-rw-r--r--indoteknik_custom/models/solr/product_product.py19
-rw-r--r--indoteknik_custom/models/solr/product_template.py5
-rw-r--r--indoteknik_custom/models/solr/promotion_program.py68
-rw-r--r--indoteknik_custom/models/solr/promotion_program_line.py69
-rw-r--r--indoteknik_custom/models/stock_picking.py37
-rw-r--r--indoteknik_custom/models/stock_quant.py8
-rw-r--r--indoteknik_custom/models/stock_warehouse_orderpoint.py3
-rw-r--r--indoteknik_custom/models/uangmuka_penjualan.py3
-rw-r--r--indoteknik_custom/models/website_user_cart.py20
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv12
-rw-r--r--indoteknik_custom/views/account_move.xml21
-rw-r--r--indoteknik_custom/views/automatic_purchase.xml6
-rw-r--r--indoteknik_custom/views/invoice_reklas_penjualan.xml40
-rw-r--r--indoteknik_custom/views/logbook_sj.xml52
-rw-r--r--indoteknik_custom/views/po_multi_cancel.xml31
-rw-r--r--indoteknik_custom/views/product_product.xml7
-rw-r--r--indoteknik_custom/views/promotion/promotion_program.xml1
-rw-r--r--indoteknik_custom/views/promotion/promotion_program_line.xml1
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml40
-rw-r--r--indoteknik_custom/views/purchase_order_multi_confirm.xml31
-rw-r--r--indoteknik_custom/views/purchase_order_multi_update.xml31
-rwxr-xr-xindoteknik_custom/views/purchase_pricelist.xml6
-rw-r--r--indoteknik_custom/views/report_logbook_sj.xml90
-rw-r--r--indoteknik_custom/views/res_partner.xml3
-rwxr-xr-xindoteknik_custom/views/sale_monitoring_detail.xml3
-rwxr-xr-xindoteknik_custom/views/sale_order.xml6
-rw-r--r--indoteknik_custom/views/stock_picking.xml2
61 files changed, 1614 insertions, 348 deletions
diff --git a/indoteknik_api/controllers/api_v1/cart.py b/indoteknik_api/controllers/api_v1/cart.py
index 8ef2c1c1..907c8288 100644
--- a/indoteknik_api/controllers/api_v1/cart.py
+++ b/indoteknik_api/controllers/api_v1/cart.py
@@ -36,18 +36,21 @@ class Cart(controller.Controller):
def create_or_update_cart(self, user_id, **kw):
# Convert input values to appropriate types
user_id = int(user_id)
- product_id = int(kw.get('product_id', 0))
- qty = int(kw.get('qty', 0))
- source = kw.get('source')
- is_selected = kw.get('selected', False)
+ product_id = kw.get('product_id', 0)
+ product_id = False if product_id == 'null' or not product_id else int(product_id)
+
program_line_id = kw.get('program_line_id', False)
program_line_id = False if program_line_id == 'null' or not program_line_id else int(program_line_id)
+ qty = int(kw.get('qty', 0))
+ source = kw.get('source')
+
+ is_selected = kw.get('selected', False)
is_selected = is_selected in ('true', True)
# Check required fields
- if not user_id or not product_id or not qty:
- return self.response(code=400, description='user_id, product_id and qty is required')
+ if not user_id:
+ return self.response(code=400, description='user_id is required')
website_user_cart = request.env['website.user.cart']
@@ -97,9 +100,15 @@ class Cart(controller.Controller):
def delete_cart_by_user_id(self, user_id, **kw):
user_id = int(user_id)
query = [('user_id', '=', user_id)]
+
+ ids = kw.get('ids')
+ if ids:
+ query += [('id', 'in', [int(x) for x in ids.split(',')])]
+
product_ids = kw.get('product_ids')
if product_ids:
query += [('product_id', 'in', [int(x) for x in product_ids.split(',')])]
+
cart = request.env['website.user.cart'].search(query).unlink()
return self.response(cart)
diff --git a/indoteknik_api/controllers/api_v1/promotion.py b/indoteknik_api/controllers/api_v1/promotion.py
index f84b8c1c..221f6e10 100644
--- a/indoteknik_api/controllers/api_v1/promotion.py
+++ b/indoteknik_api/controllers/api_v1/promotion.py
@@ -6,6 +6,28 @@ from datetime import datetime
class Promotion(controller.Controller):
prefix = '/api/v1/'
+
+ @http.route(prefix + 'program-line/<id>/stock', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_promotion_stock(self, id):
+ program_line = request.env['promotion.program.line'].browse(int(id))
+ if not program_line.id:
+ return self.response(code=400, description='program not found')
+
+ user_data = self.verify_user_token()
+
+ limit_qty = program_line._res_limit_qty()
+ remaining_qty = program_line._get_remaining_qty(user_data)
+
+ percent_remaining = 0
+ if limit_qty['all'] > 0:
+ percent_remaining = (limit_qty['all'] - remaining_qty['all']) / limit_qty['all'] * 100
+
+ return self.response({
+ 'limit_qty': limit_qty,
+ 'remaining_qty': remaining_qty,
+ 'used_percentage': percent_remaining,
+ })
@http.route(prefix + 'promotion/<id>', auth='public', methods=['GET'])
diff --git a/indoteknik_api/controllers/controller.py b/indoteknik_api/controllers/controller.py
index 4d6716b2..50e86b68 100644
--- a/indoteknik_api/controllers/controller.py
+++ b/indoteknik_api/controllers/controller.py
@@ -1,14 +1,17 @@
-from array import array
-import datetime
import base64
+import datetime
+import functools
+import io
import json
+from array import array
+import jwt
from odoo import http
from odoo.http import request
+from odoo.modules import get_module_resource
from odoo.tools.config import config
+from PIL import Image
from pytz import timezone
-import jwt
-import functools
class Controller(http.Controller):
@@ -182,8 +185,68 @@ class Controller(http.Controller):
return self.response(address)
@http.route('/api/image/<model>/<field>/<id>', auth='public', methods=['GET'])
- def get_image(self, model, field, id):
+ def get_image(self, model, field, id, **kw):
+ model_name = model
model = request.env[model].sudo().search([('id', '=', id)], limit=1)
- image = model[field] if model[field] else ''
- request.env['user.activity.log'].record_activity()
- return request.make_response(base64.b64decode(image), [('Content-Type', 'image/jpg')])
+ image = model[field] if field in model else ''
+
+ if model_name in ['product.template']:
+ version = '1' if field in ['image_256', 'image_512', 'image_1024', 'image_1920'] else '2'
+ ratio = kw.get('ratio', '')
+ image = model['image_256'] or ''
+ image = self.add_watermark_to_image(image, ratio, version)
+
+ response_headers = [
+ ('Content-Type', 'image/jpg'),
+ ('Cache-Control', 'public, max-age=3600')
+ ]
+
+ return request.make_response(
+ base64.b64decode(image),
+ response_headers
+ )
+
+
+ def add_watermark_to_image(self, image, ratio, version = '1'):
+ if not image: return ''
+
+ logo_path = get_module_resource('indoteknik_api', 'static', 'src', 'images', 'logo-indoteknik-gray.png')
+ logo_img = Image.open(logo_path).convert('RGBA')
+
+ img_data = io.BytesIO(base64.b64decode(image))
+ img = Image.open(img_data).convert('RGBA')
+
+ img_width, img_height = img.size
+ longest_wh = max(img_height, img_width)
+
+ # Resize logo image
+ logo_img_w = img_width // 2.2
+ logo_img_h = img_height // 2.2
+
+ new_img = img
+
+ if ratio == 'square':
+ new_img = Image.new('RGBA', (longest_wh, longest_wh), (255, 255, 255, 255))
+
+ paste_x = (longest_wh - img_width) // 2
+ paste_y = (longest_wh - img_height) // 2
+
+ new_img.paste(img, (paste_x, paste_y), img)
+
+ if version == '2':
+ logo__footer_path = get_module_resource('indoteknik_api', 'static', 'src', 'images', 'logo-indoteknik-footer.png')
+ logo_footer_img = Image.open(logo__footer_path).convert('RGBA')
+ logo_footer_img.thumbnail((img_width, img_height // 1))
+ logo_footer_w, logo_footer_h = logo_footer_img.size
+ new_img.paste(logo_footer_img, (0, img_height - logo_footer_h - 20), logo_footer_img)
+
+ logo_img.thumbnail((logo_img_w, logo_img_h))
+
+ if version == '1':
+ # Add watermark
+ new_img.paste(logo_img, (12, 10), logo_img)
+
+ buffered = io.BytesIO()
+ new_img.save(buffered, format="PNG")
+ return base64.b64encode(buffered.getvalue()).decode('utf-8')
+
diff --git a/indoteknik_api/static/src/fonts/Inter.ttf b/indoteknik_api/static/src/fonts/Inter.ttf
new file mode 100644
index 00000000..1cb674b7
--- /dev/null
+++ b/indoteknik_api/static/src/fonts/Inter.ttf
Binary files differ
diff --git a/indoteknik_api/static/src/images/logo-indoteknik-footer.png b/indoteknik_api/static/src/images/logo-indoteknik-footer.png
new file mode 100644
index 00000000..fcd1ba70
--- /dev/null
+++ b/indoteknik_api/static/src/images/logo-indoteknik-footer.png
Binary files differ
diff --git a/indoteknik_api/static/src/images/logo-indoteknik-gray.png b/indoteknik_api/static/src/images/logo-indoteknik-gray.png
new file mode 100644
index 00000000..3039ca38
--- /dev/null
+++ b/indoteknik_api/static/src/images/logo-indoteknik-gray.png
Binary files differ
diff --git a/indoteknik_api/static/src/images/logo-indoteknik.png b/indoteknik_api/static/src/images/logo-indoteknik.png
new file mode 100644
index 00000000..e669699c
--- /dev/null
+++ b/indoteknik_api/static/src/images/logo-indoteknik.png
Binary files differ
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index e7793f1c..333596a3 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -107,6 +107,12 @@
'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',
+ 'views/po_multi_cancel.xml',
+ 'views/logbook_sj.xml',
+ 'views/report_logbook_sj.xml',
'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 f1d0a6cf..720a4f91 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -81,12 +81,12 @@ from . import po_sync_price
from . import base_import_import
from . import product_attribute
from . import mrp_production
+from . import promotion
from . import solr
from . import cost_centre
from . import account_account
from . import account_move_line
from . import stock_scheduler_compute
-from . import promotion
from . import sale_orders_multi_update
from . import quotation_so_multi_update
from . import product_monitoring
@@ -98,4 +98,11 @@ 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 \ No newline at end of file
+from . import sale_monitoring_detail_v2
+from . import purchase_order_multi_update
+from . import invoice_reklas_penjualan
+from . import purchase_order_multi_confirm
+from . import stock_quant
+from . import po_multi_cancel
+from . import logbook_sj
+from . import report_logbook_sj
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index 82a86a39..a9db212f 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -35,6 +35,34 @@ class AccountMove(models.Model):
('belum_upload', 'Belum Upload FP'),
('sudah_upload', 'Sudah Upload FP'),
], 'Mark Upload Faktur', compute='_compute_mark_upload_efaktur', default='belum_upload')
+ sale_id = fields.Many2one('sale.order', string='Sale Order')
+ reklas_id = fields.Many2one('account.move', string='Nomor CAB', domain="[('partner_id', '=', partner_id)]")
+ new_invoice_day_to_due = fields.Integer(string="New Day Due", compute="_compute_invoice_day_to_due")
+ date_efaktur_upload = fields.Datetime(string='eFaktur Upload Date', tracking=True)
+
+ @api.constrains('efaktur_document')
+ def _constrains_date_efaktur(self):
+ for move in self:
+ current_time = datetime.utcnow()
+ move.date_efaktur_upload = current_time
+
+ def open_form_multi_create_reklas_penjualan(self):
+ action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_view_invoice_reklas_penjualan')
+ invoice = self.env['invoice.reklas.penjualan'].create([{
+ 'name': '-',
+ }])
+ for move in self:
+ sale_id = move.sale_id.id
+ self.env['invoice.reklas.penjualan.line'].create([{
+ 'invoice_reklas_id': invoice.id,
+ 'name': move.name,
+ 'partner_id': move.partner_id.id,
+ 'sale_id': move.sale_id.id,
+ 'amount_untaxed_signed': move.amount_untaxed_signed,
+ 'amount_total_signed': move.amount_total_signed,
+ }])
+ action['res_id'] = invoice.id
+ return action
def _compute_mark_upload_efaktur(self):
for move in self:
@@ -112,12 +140,16 @@ class AccountMove(models.Model):
def _compute_invoice_day_to_due(self):
for invoice in self:
invoice_day_to_due = 0
+ new_invoice_day_to_due = 0
if invoice.payment_state not in ['paid', 'in_payment', 'reversed'] and invoice.invoice_date_due:
invoice_day_to_due = invoice.invoice_date_due - date.today()
+ new_invoice_day_to_due = invoice.invoice_date_due - date.today()
if invoice.new_due_date:
invoice_day_to_due = invoice.new_due_date - date.today()
invoice_day_to_due = invoice_day_to_due.days
+ new_invoice_day_to_due = new_invoice_day_to_due.days
invoice.invoice_day_to_due = invoice_day_to_due
+ invoice.new_invoice_day_to_due = new_invoice_day_to_due
def _compute_bill_day_to_due(self):
for rec in self:
diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py
index 94dd9cdf..5919467f 100644
--- a/indoteknik_custom/models/automatic_purchase.py
+++ b/indoteknik_custom/models/automatic_purchase.py
@@ -68,8 +68,6 @@ class AutomaticPurchase(models.Model):
if self.is_po:
raise UserError('Sudah pernah di create PO')
- # self.validate_so_is_po()
-
current_time = datetime.now()
vendor_ids = self.env['automatic.purchase.line'].read_group(
[('automatic_purchase_id', '=', self.id), ('partner_id', '!=', False)],
@@ -389,7 +387,7 @@ class AutomaticPurchase(models.Model):
], order='count_trx_po desc, count_trx_po_vendor desc', limit=1)
vendor_id = purchase_price.vendor_id.id
- price = self._get_valid_purchase_price(purchase_price)
+ price, taxes = self._get_valid_purchase_price(purchase_price)
if self.vendor_id and self.vendor_id.id != vendor_id:
continue
@@ -405,6 +403,7 @@ class AutomaticPurchase(models.Model):
# 'last_price': po_line.price_unit,
'partner_id': vendor_id,
'last_price': price,
+ 'taxes_id': taxes,
'subtotal': qty_purchase * price,
'last_order_id': po_line.order_id.id,
'last_orderline_id': po_line.id,
@@ -420,12 +419,12 @@ class AutomaticPurchase(models.Model):
human_last_update = purchase_price.human_last_update or datetime.min
system_last_update = purchase_price.system_last_update or datetime.min
+ price = purchase_price.product_price
+ taxes = purchase_price.taxes_product_id.id
+
if system_last_update > human_last_update:
price = purchase_price.system_price
taxes = purchase_price.taxes_system_id.id
- else:
- price = purchase_price.product_price
- taxes = purchase_price.taxes_product_id.id
return price, taxes
diff --git a/indoteknik_custom/models/base_import_import.py b/indoteknik_custom/models/base_import_import.py
index 9cffa6e6..6a100cb8 100644
--- a/indoteknik_custom/models/base_import_import.py
+++ b/indoteknik_custom/models/base_import_import.py
@@ -1,45 +1,52 @@
+from datetime import datetime
+
from odoo import models
from odoo.exceptions import UserError
-from datetime import datetime
+from pytz import timezone
+
class Import(models.TransientModel):
_inherit = 'base_import.import'
+
+ def _get_config(self, key, default = ''):
+ return self.env['ir.config_parameter'].sudo().get_param('base_import.import.' + key, default)
def _get_config_enable_import(self):
- return self.env['ir.config_parameter'].sudo().get_param('base_import.import.enable_import', '')
+ return self._get_config('enable_import')
+
+ def _get_config_allowed_model(self):
+ return self._get_config('allowed_model')
- def _get_config_restrict_model(self):
- return self.env['ir.config_parameter'].sudo().get_param('base_import.import.restrict_model', '')
+ def _get_config_always_allowed_model(self):
+ return self._get_config('always_allowed_model')
def _check_time_range(self, config_string):
- current_time = datetime.now().time()
+ current_time = datetime.now(tz=timezone('Asia/Jakarta')).time()
ranges = config_string.split(' / ')
for time_range in ranges:
start_str, end_str = time_range.split(' - ')
start_time = datetime.strptime(start_str, '%H:%M:%S').time()
end_time = datetime.strptime(end_str, '%H:%M:%S').time()
-
- if start_time <= current_time and current_time <= end_time:
+
+ # If current time in range of start and end time
+ if current_time >= start_time and current_time <= end_time:
return True
return False
def _check_enable_import(self):
enable_import = self._get_config_enable_import()
- restrict_model = self._get_config_restrict_model()
-
- if self.res_model not in restrict_model:
- return True
+ allowed_model = self._get_config_allowed_model()
+ always_allowed_model = self._get_config_always_allowed_model()
- if enable_import.lower() == 'true':
+ if self.res_model in always_allowed_model or always_allowed_model == 'ALL':
return True
- elif enable_import.lower() == 'false':
- return False
- elif '-' in enable_import:
+
+ if self.res_model in allowed_model or allowed_model == 'ALL':
return self._check_time_range(enable_import)
- return True
+ return False
def _unable_import_notif(self):
enable_import_config = self._get_config_enable_import()
diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py
index a9ad624c..955d1634 100644
--- a/indoteknik_custom/models/commision.py
+++ b/indoteknik_custom/models/commision.py
@@ -159,12 +159,14 @@ class CustomerCommision(models.Model):
@api.constrains('commision_percent')
def _onchange_commision_percent(self):
print('masuk onchange commision percent')
- self.commision_amt = self.commision_percent * self.total_dpp // 100
+ if self.commision_amt == 0:
+ self.commision_amt = self.commision_percent * self.total_dpp // 100
- # @api.constrains('commision_amt')
- # def _onchange_commision_amt(self):
- # print('masuk onchange commision amt')
- # self.commision_percent = (self.commision_amt / self.grand_total * 100)
+ @api.constrains('commision_amt')
+ def _onchange_commision_amt(self):
+ print('masuk onchange commision amt')
+ if self.commision_percent == 0:
+ self.commision_percent = (self.commision_amt / self.total_dpp * 100)
def _compute_total_dpp(self):
for data in self:
@@ -176,6 +178,11 @@ class CustomerCommision(models.Model):
@api.model
def create(self, vals):
vals['number'] = self.env['ir.sequence'].next_by_code('customer.commision') or '0'
+ # if vals['commision_amt'] > 0:
+ # commision_amt = vals['commision_amt']
+ # total_dpp = vals['total_dpp']
+ # commision_percent = commision_amt / total_dpp * 100
+ # vals['commision_percent'] = commision_percent
result = super(CustomerCommision, self).create(vals)
return result
diff --git a/indoteknik_custom/models/crm_lead.py b/indoteknik_custom/models/crm_lead.py
index d181c35b..e8721142 100755
--- a/indoteknik_custom/models/crm_lead.py
+++ b/indoteknik_custom/models/crm_lead.py
@@ -80,18 +80,11 @@ class CrmLead(models.Model):
input_tags.append(1510) #no tag
lead.tag_ids = input_tags
- if not lead.user_id or lead.user_id.id in [25]:
- partner = lead.partner_id.parent_id or lead.partner_id
- if partner.user_id and partner.user_id.id not in [25]:
- salesperson_id = partner.user_id.id
- else:
- salesperson_id = 2
- else:
- partner = lead.partner_id.parent_id or lead.partner_id
- if partner.user_id and partner.user_id.id not in [25]:
- salesperson_id = partner.user_id.id
- else:
- salesperson_id = 2
+ salesperson_id = 2
+
+ partner = lead.partner_id.parent_id or lead.partner_id
+ if partner.user_id and partner.user_id.id not in [25]:
+ salesperson_id = partner.user_id.id
lead.user_id = salesperson_id
diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py
index c2ee7e3d..30da02d1 100644
--- a/indoteknik_custom/models/invoice_reklas.py
+++ b/indoteknik_custom/models/invoice_reklas.py
@@ -94,3 +94,4 @@ class InvoiceReklas(models.TransientModel):
'type': 'ir.actions.act_window',
'res_id': account_move.id
}
+ \ No newline at end of file
diff --git a/indoteknik_custom/models/invoice_reklas_penjualan.py b/indoteknik_custom/models/invoice_reklas_penjualan.py
new file mode 100644
index 00000000..5027c8af
--- /dev/null
+++ b/indoteknik_custom/models/invoice_reklas_penjualan.py
@@ -0,0 +1,79 @@
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+from datetime import datetime
+from odoo.http import request
+
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class InvoiceReklasPenjualan(models.TransientModel):
+ _name = 'invoice.reklas.penjualan'
+ _description = "digunakan untuk reklas Uang Muka Penjualan"
+
+ name = fields.Char(string='Name')
+ invoice_reklas_line = fields.One2many('invoice.reklas.penjualan.line', 'invoice_reklas_id', string='Invoice Reklas Line')
+
+ def create_reklas_penjualan(self):
+ invoices = self.invoice_reklas_line
+
+ current_time = datetime.now()
+ account_move_ids = []
+ for invoice in invoices:
+ ref_name = 'REKLAS ' + invoice.reklas_id.name + " UANG MUKA PENJUALAN " + invoice.name + " " + invoice.partner_id.name
+ parameters_header = {
+ 'ref': ref_name,
+ 'date': current_time,
+ 'journal_id': 13
+ }
+
+ account_move = self.env['account.move'].create([parameters_header])
+ _logger.info('Success Reklas with %s' % account_move.name)
+
+ parameter_debit = {
+ 'move_id': account_move.id,
+ 'account_id': 449, # uang muka penjualan
+ 'partner_id': invoice.partner_id.id,
+ 'currency_id': 12,
+ 'debit': invoice.pay_amt,
+ 'credit': 0,
+ 'name': ref_name
+ }
+ parameter_credit = {
+ 'move_id': account_move.id,
+ 'account_id': 395,
+ 'partner_id': invoice.partner_id.id,
+ 'currency_id': 12,
+ 'debit': 0,
+ 'credit': invoice.pay_amt,
+ 'name': ref_name
+ }
+ self.env['account.move.line'].create([parameter_debit, parameter_credit])
+ account_move_ids.append(account_move.id)
+ invoice.unlink()
+
+ self.unlink()
+ return {
+ 'name': _('Journal Entries'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'account.move',
+ 'target': 'current',
+ 'type': 'ir.actions.act_window',
+ 'domain': [('id', 'in', account_move_ids)],
+ }
+
+class InvoiceReklasPenjualanLine(models.TransientModel):
+ _name = 'invoice.reklas.penjualan.line'
+ _description = "digunakan untuk reklas Uang Muka Penjualan"
+
+ invoice_reklas_id = fields.Many2one('invoice.reklas.penjualan', string='Invoice Reklas')
+ pay_amt = fields.Float(string='Yang dibayarkan')
+ reklas_id = fields.Many2one('account.move', string='Nomor CAB', domain="[('sale_id', '=', sale_id)]")
+ sale_id = fields.Many2one('sale.order', string='Sale Order')
+ amount_untaxed_signed = fields.Float(string='Tax excluded')
+ amount_total_signed = fields.Float(string='Total')
+ name = fields.Char(string='Name')
+ partner_id = fields.Many2one('res.partner', string='Partner')
+
+
diff --git a/indoteknik_custom/models/logbook_sj.py b/indoteknik_custom/models/logbook_sj.py
new file mode 100644
index 00000000..567f1ae3
--- /dev/null
+++ b/indoteknik_custom/models/logbook_sj.py
@@ -0,0 +1,117 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import UserError
+from pytz import timezone
+from datetime import datetime
+
+class LogbookSJ(models.TransientModel):
+ _name = 'logbook.sj'
+
+ name = fields.Char(string='Name', default='Logbook SJ')
+ logbook_sj_line = fields.One2many(
+ comodel_name='logbook.sj.line',
+ inverse_name='logbook_sj_id',
+ string='Logbook SJ Line'
+ )
+
+ def create_logbook_sj(self):
+ logbook_line = self.logbook_sj_line
+
+ current_time = datetime.utcnow()
+ report_logbook_ids = []
+ for line in logbook_line:
+ nomor_sj = line.name
+ picking = self.env['stock.picking'].search([
+ ('picking_code', '=', nomor_sj),
+ ])
+ parameters_header = {
+ 'name': nomor_sj,
+ 'date': current_time,
+ 'name_picking': picking.name,
+ 'partner_id': picking.partner_id.id,
+ }
+
+ report_logbook = self.env['report.logbook.sj'].create([parameters_header])
+
+
+ for stock in picking.move_line_ids_without_package:
+ data = {
+ 'product_id': stock.product_id.id,
+ 'location_id': stock.location_id.id,
+ 'product_uom_qty': stock.product_uom_qty,
+ 'qty_done': stock.qty_done,
+ 'product_uom_id': stock.product_uom_id.id,
+ 'report_logbook_sj_id': report_logbook.id
+ }
+ self.env['report.logbook.sj.line'].create([data])
+
+ report_logbook_ids.append(report_logbook.id)
+ line.unlink()
+
+ self.unlink()
+ return {
+ 'name': _('Report Logbook SJ'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'report.logbook.sj',
+ 'target': 'current',
+ 'type': 'ir.actions.act_window',
+ 'domain': [('id', 'in', report_logbook_ids)],
+ }
+
+class LogbookSJLine(models.TransientModel):
+ _name = 'logbook.sj.line'
+
+ name = fields.Char(string='SJ Number')
+ driver_id = fields.Many2one(comodel_name='res.users', string='Driver')
+ departure_date = fields.Char(string='Departure Date')
+ arrival_date = fields.Char(string='Arrival Date')
+ carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method')
+ tracking_no = fields.Char(string='Tracking No')
+ logbook_sj_id = fields.Many2one('logbook.sj', string='Logbook SJ')
+ partner_id = fields.Many2one('res.partner', string='Customer')
+
+ @api.onchange('name')
+ def onchange_name(self):
+ current_time = datetime.now(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S')
+
+ if self.name:
+ if len(self.name) == 13:
+ self.name = self.name[:-1]
+ picking = self.env['stock.picking'].search([('picking_code', '=', self.name)], limit=1)
+ if picking:
+ if picking.driver_id:
+ self.driver_id = picking.driver_id
+ else:
+ self.driver_id = self.env.uid
+
+ sale_order = False
+ if picking.origin:
+ sale_order = self.env['sale.order'].search([('name', '=', picking.origin)], limit=1)
+
+ if sale_order.carrier_id:
+ self.carrier_id = sale_order.carrier_id
+
+ self.tracking_no = picking.delivery_tracking_no
+
+ self.partner_id = picking.partner_id
+
+ delivery_type = self.get_delivery_type(picking.driver_departure_date, picking.driver_arrival_date)
+ if delivery_type != 'departure':
+ self.departure_date = picking.driver_departure_date.astimezone(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S')
+
+ if delivery_type == 'departure':
+ self.departure_date = current_time
+ elif delivery_type == 'arrival':
+ self.arrival_date = current_time
+ else:
+ self.arrival_date = picking.driver_arrival_date.astimezone(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S')
+ else:
+ raise UserError('Nomor DO tidak ditemukan')
+
+ def get_delivery_type(self, driver_departure_date, driver_arrival_date):
+ delivery_type = 'departure'
+ if driver_departure_date:
+ delivery_type = 'arrival'
+ if driver_arrival_date:
+ delivery_type = False
+
+ return delivery_type
diff --git a/indoteknik_custom/models/po_multi_cancel.py b/indoteknik_custom/models/po_multi_cancel.py
new file mode 100644
index 00000000..52c156b5
--- /dev/null
+++ b/indoteknik_custom/models/po_multi_cancel.py
@@ -0,0 +1,22 @@
+from odoo import models, fields
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class PoMultiCancel(models.TransientModel):
+ _name = 'po.multi.cancel'
+
+ def save_multi_cancel_po(self):
+ purchase_ids = self._context['purchase_ids']
+ purchase = self.env['purchase.order'].browse(purchase_ids)
+ purchase.button_cancel()
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': 'Notification',
+ 'message': 'Status berhasil diubah',
+ 'next': {'type': 'ir.actions.act_window_close'},
+ }
+ } \ No newline at end of file
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index 7ec5a002..d2fa793a 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -74,32 +74,34 @@ class ProductTemplate(models.Model):
@api.constrains('name', 'default_code')
def _check_duplicate_product(self):
for product in self:
- if not self.env.user.is_purchasing_manager and not self.env.user.is_editor_product:
- domain = [('default_code', '!=', False)]
-
- if product.product_variant_ids:
- domain.extend([
- '|',
- ('name', 'in', [variants.name for variants in product.product_variant_ids]),
- ('default_code', 'in', [variants.default_code for variants in product.product_variant_ids])
- ])
- else:
- domain.extend([
- '|',
- ('name', 'in', [product.name]),
- ('default_code', 'in', [product.default_code])
- ])
-
- domain.append(('id', '!=', product.id))
-
- if product.write_date == product.create_date:
- message = "SKU atau Name yang Anda gunakan sudah digunakan di produk lain"
- elif all(day_product > 0 for day_product in product.day_product_to_edit()):
- domain = [('id', '=', product.id)]
- message = "Hanya Pak Tyas yang dapat merubah data produk"
- existing_purchase = self.search(domain, limit=1)
- if existing_purchase:
- raise UserError(message)
+ if self.env.user.is_purchasing_manager or self.env.user.is_editor_product or self.env.user.id in [1, 25]:
+ continue
+
+ domain = [('default_code', '!=', False)]
+
+ if product.product_variant_ids:
+ domain.extend([
+ '|',
+ ('name', 'in', [variants.name for variants in product.product_variant_ids]),
+ ('default_code', 'in', [variants.default_code for variants in product.product_variant_ids])
+ ])
+ else:
+ domain.extend([
+ '|',
+ ('name', 'in', [product.name]),
+ ('default_code', 'in', [product.default_code])
+ ])
+
+ domain.append(('id', '!=', product.id))
+
+ if product.write_date == product.create_date:
+ message = "SKU atau Name yang Anda gunakan sudah digunakan di produk lain"
+ elif all(day_product > 0 for day_product in product.day_product_to_edit()):
+ domain = [('id', '=', product.id)]
+ message = "Anda tidak berhak merubah data produk ini"
+ existing_purchase = self.search(domain, limit=1)
+ if existing_purchase:
+ raise UserError(message)
@api.constrains('name')
def _validate_name(self):
@@ -157,28 +159,34 @@ class ProductTemplate(models.Model):
_logger.info('Updated New Product %s' % product.name)
def update_internal_reference(self, limit=100):
- templates_without_variant = self.env['product.template'].search([
- ('default_code', '=', False),
- ('type', '=', 'product'),
- ('active', '=', True),
- ('product_variant_ids', '=', False),
- ], limit=limit)
- for template_without_variant in templates_without_variant:
- template_without_variant.default_code = 'IT.'+str(template_without_variant.id)
- _logger.info('Updated Template %s' % template_without_variant.name)
-
- templates_with_variant = self.env['product.template'].search([
+ templates = self.env['product.template'].search([
('default_code', '=', False),
+ ('product_variant_ids.default_code', '=', False),
('type', '=', 'product'),
- ('active', '=', True),
- ('product_variant_ids', '!=', False),
- ], limit=limit)
- for template_with_variant in templates_with_variant:
- for product in template_with_variant.product_variant_ids:
- if product.default_code:
- continue
- product.default_code = 'ITV.'+str(product.id)
- _logger.info('Updated Variant %s' % product.name)
+ ('active', '=', True)
+ ], limit=limit, order='write_date desc')
+ for template in templates:
+ if not template.default_code:
+ template.default_code = 'IT.'+str(template.id)
+
+ for variant in template.product_variant_ids:
+ if not variant.default_code:
+ variant.default_code = 'ITV.%s' % str(variant.id)
+
+ _logger.info('Updated Template %s' % template.name)
+
+ # templates_with_variant = self.env['product.product'].search([
+ # ('default_code', '=', False),
+ # ('type', '=', 'product'),
+ # ('active', '=', True),
+ # ('product_tmpl_id', '!=', False),
+ # ], limit=limit, order='write_date desc')
+ # for template_with_variant in templates_with_variant:
+ # for product in template_with_variant.product_variant_ids:
+ # if product.default_code:
+ # continue
+ # product.default_code = 'ITV.'+str(product.id)
+ # _logger.info('Updated Variant %s' % product.name)
@api.onchange('name','default_code','x_manufacture','product_rating','website_description','image_1920','weight','public_categ_ids')
def update_solr_flag(self):
@@ -339,10 +347,11 @@ class ProductProduct(models.Model):
usage = fields.Char(string='Usage')
specification = fields.Char(string='Specification')
material = fields.Char(string='Material')
- qty_onhand_bandengan = fields.Float(string='Qty Onhand Bandengan', compute='_get_qty_onhand_bandengan')
- qty_incoming_bandengan = fields.Float(string='Qty Incoming Bandengan', compute='_get_qty_incoming_bandengan')
- qty_outgoing_bandengan = fields.Float(string='Qty Outgoing Bandengan', compute='_get_qty_outgoing_bandengan')
- qty_available_bandengan = fields.Float(string='Qty Available Bandengan', compute='_get_qty_available_bandengan')
+ qty_onhand_bandengan = fields.Float(string='Onhand BU', compute='_get_qty_onhand_bandengan')
+ qty_incoming_bandengan = fields.Float(string='Incoming BU', compute='_get_qty_incoming_bandengan')
+ qty_outgoing_bandengan = fields.Float(string='Outgoing BU', compute='_get_qty_outgoing_bandengan')
+ qty_available_bandengan = fields.Float(string='Available BU', compute='_get_qty_available_bandengan')
+ qty_free_bandengan = fields.Float(string='Free BU', compute='_get_qty_free_bandengan')
qty_upcoming = fields.Float(string='Qty Upcoming', compute='_get_qty_upcoming')
sla_version = fields.Integer(string="SLA Version", default=0)
is_edited = fields.Boolean(string='Is Edited')
@@ -377,24 +386,26 @@ class ProductProduct(models.Model):
@api.constrains('name','default_code')
def _check_duplicate_product(self):
for product in self:
- if not self.env.user.is_purchasing_manager and not self.env.user.is_editor_product:
- if product.write_date == product.create_date:
- domain = [
- ('default_code', '!=', False),
- '|',
- ('name', 'in', [template.name for template in product.product_tmpl_id] or [product.name]),
- ('default_code', 'in', [template.default_code for template in product.product_tmpl_id] or [product.default_code])]
-
- domain.append(('id', '!=', product.id))
- massage="SKU atau Name yang anda pakai sudah digunakan di product lain"
- existing_purchase = self.search(domain, limit=1)
- if existing_purchase:
- raise UserError(massage)
- elif all(day_product > 0 for day_product in product.day_product_to_edit()):
- domain = [('id', '=', product.id)]
- existing_purchase = self.search(domain)
- if existing_purchase:
- raise UserError('Hanya Pak Tyas Yang Dapat Merubah Data Product')
+ if self.env.user.is_purchasing_manager or self.env.user.is_editor_product or self.env.user.id in [1, 25]:
+ continue
+
+ if product.write_date == product.create_date:
+ domain = [
+ ('default_code', '!=', False),
+ '|',
+ ('name', 'in', [template.name for template in product.product_tmpl_id] or [product.name]),
+ ('default_code', 'in', [template.default_code for template in product.product_tmpl_id] or [product.default_code])]
+
+ domain.append(('id', '!=', product.id))
+ massage="SKU atau Name yang anda pakai sudah digunakan di product lain"
+ existing_purchase = self.search(domain, limit=1)
+ if existing_purchase:
+ raise UserError(massage)
+ elif all(day_product > 0 for day_product in product.day_product_to_edit()):
+ domain = [('id', '=', product.id)]
+ existing_purchase = self.search(domain)
+ if existing_purchase:
+ raise UserError('Anda tidak berhak merubah data product ini')
@api.constrains('name')
def _validate_name(self):
@@ -439,6 +450,11 @@ class ProductProduct(models.Model):
for product in self:
qty_available = product.qty_incoming_bandengan + product.qty_onhand_bandengan - product.qty_outgoing_bandengan
product.qty_available_bandengan = qty_available
+
+ def _get_qty_free_bandengan(self):
+ for product in self:
+ qty_free = product.qty_onhand_bandengan - product.qty_outgoing_bandengan
+ product.qty_free_bandengan = qty_free
# def write(self, vals):
# if 'solr_flag' not in vals:
diff --git a/indoteknik_custom/models/promotion/promotion_program.py b/indoteknik_custom/models/promotion/promotion_program.py
index 29aaa753..1a1ee6bf 100644
--- a/indoteknik_custom/models/promotion/promotion_program.py
+++ b/indoteknik_custom/models/promotion/promotion_program.py
@@ -6,6 +6,7 @@ class PromotionProgram(models.Model):
name = fields.Char(string="Name")
banner = fields.Binary(string="Banner")
+ image = fields.Binary(string="Image")
icon = fields.Binary(string="Icon", help="Image 1:1 ratio")
icon_top = fields.Binary(string="Icon Top", help="Icon ini ditampilkan sebagai atribut pada atas gambar di product card pada website")
icon_bottom = fields.Binary(string="Icon Bottom", help="Icon ini ditampilkan sebagai atribut pada bawah gambar di product card pada website")
diff --git a/indoteknik_custom/models/promotion/promotion_program_line.py b/indoteknik_custom/models/promotion/promotion_program_line.py
index 34a0fbb2..18a7e184 100644
--- a/indoteknik_custom/models/promotion/promotion_program_line.py
+++ b/indoteknik_custom/models/promotion/promotion_program_line.py
@@ -13,7 +13,6 @@ class PromotionProgramLine(models.Model):
("discount_loading", "Discount Loading"),
("merchandise", "Merchandise")
], 'Type')
- image = fields.Binary(string="Image")
package_limit = fields.Integer('Package limit')
package_limit_user = fields.Integer('Package limit / user')
@@ -21,6 +20,7 @@ class PromotionProgramLine(models.Model):
product_ids = fields.One2many('promotion.product', 'program_line_id', 'Product')
free_product_ids = fields.One2many('promotion.free_product', 'program_line_id', 'Free Product')
+ display_on_homepage = fields.Boolean('Display on Homepage')
price = fields.Float('Price')
discount_type = fields.Selection([
@@ -96,16 +96,21 @@ class PromotionProgramLine(models.Model):
weight = 0
if not any(x['package_weight'] == 0 for x in merged_products):
weight = sum(x['package_weight'] for x in merged_products)
-
+
+ # Sum of products and free products in 1 package quantity
+ products_total = sum(x['price']['price_discount'] * x['qty'] / qty for x in products)
+ free_products_total = sum(x['price']['price_discount'] * x['qty'] / qty for x in free_products)
+ package_price = products_total + free_products_total
+
response = {
'id': self.id,
'name': self.name,
- 'image': ir_attachment.api_image(self._name, 'image', self.id),
'remaining_time': self._get_remaining_time(),
'promotion_type': self._res_promotion_type(),
'limit_qty': limit_qty,
'remaining_qty': remaining_qty,
'used_percentage': percent_remaining,
+ 'package_price': package_price,
'price': {
'price': self.price,
'price_discount': self.price,
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 82ca3108..99d79072 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -46,12 +46,65 @@ class PurchaseOrder(models.Model):
summary_qty_po = fields.Float('Total Qty', compute='_compute_summary_qty')
summary_qty_receipt = fields.Float('Summary Qty Receipt', compute='_compute_summary_qty')
count_line_product = fields.Float('Total Item', compute='compute_count_line_product')
- note_description = fields.Char(string='Note', help='bisa diisi sebagai informasi indent barang tertentu atau apapun')
+ note_description = fields.Char(string='Noteman', help='bisa diisi sebagai informasi indent barang tertentu atau apapun')
has_active_invoice = fields.Boolean(string='Has Active Invoice', compute='_compute_has_active_invoice')
description = fields.Char(string='Description', help='bisa diisi sebagai informasi indent barang tertentu atau apapun')
purchase_order_lines = fields.One2many('purchase.order.line', 'order_id', string='Indent', auto_join=True)
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)
+
+ @api.model
+ def action_multi_cancel(self):
+ for purchase in self:
+ purchase.update({
+ 'state': 'cancel',
+ })
+
+ if purchase.state == 'cancel':
+ purchase.update({
+ 'approval_status': False,
+ })
+
+ def open_form_multi_cancel(self):
+ action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_po_multi_cancel')
+ action['context'] = {
+ 'purchase_ids': [x.id for x in self]
+ }
+ return action
+
+ def delete_line(self):
+ lines_to_delete = self.order_line.filtered(lambda line: line.suggest == 'masih cukup')
+ if not lines_to_delete:
+ raise UserError('Tidak ada item yang masih cukup')
+ lines_to_delete.unlink()
+
+ def open_form_multi_confirm_po(self):
+ action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_purchase_order_multi_confirm')
+ action['context'] = {
+ 'order_ids': [x.id for x in self]
+ }
+ return action
+
+ def action_multi_update_paid_status(self):
+ for purchase in self:
+ purchase.update({
+ 'status_paid_cbd': True,
+ })
+
+ def action_multi_confirm_po(self):
+ for purchase in self:
+ if purchase.state != 'draft':
+ continue
+
+ purchase.button_confirm()
+
+ def open_form_multi_update_paid_status(self):
+ action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_purchase_order_multi_update')
+ action['context'] = {
+ 'purchase_ids': [x.id for x in self]
+ }
+ return action
def _compute_responsibles(self):
for purchase in self:
@@ -70,15 +123,19 @@ class PurchaseOrder(models.Model):
for line in self.order_line:
i += 1
current_time = datetime.utcnow()
- print(i, len(self.order_line))
+ # print(i, len(self.order_line))
+
price_unit = line.price_unit
taxes = line.taxes_id
- for tax in taxes:
- tax_include = tax.price_include
- tax_amt = tax.amount
- if taxes:
- if not tax_include:
- price_unit = price_unit + (price_unit * tax_amt / 100)
+ # for tax in taxes:
+ # tax_include = tax.price_include
+ # if taxes:
+ # if tax_include:
+ # price_unit = price_unit
+ # else:
+ # price_unit = price_unit + (price_unit * 11 / 100)
+ # else:
+ # price_unit = price_unit + (price_unit * 11 / 100)
purchase_pricelist = self.env['purchase.pricelist'].search([
('product_id', '=', line.product_id.id),
@@ -89,21 +146,24 @@ class PurchaseOrder(models.Model):
purchase_pricelist.create([{
'vendor_id': line.order_id.partner_id.id,
'product_id': line.product_id.id,
- 'product_price': 0.00,
+ 'product_price': 0,
+ 'taxes_system_id': taxes.id,
'system_price': price_unit,
- 'system_last_update': current_time,
+ 'system_last_update': current_time
}])
else:
purchase_pricelist.write({
'system_last_update': current_time,
+ 'taxes_system_id': taxes.id,
'system_price': price_unit
})
def _compute_date_planned(self):
for order in self:
if order.date_approve:
+ leadtime = order.partner_id.leadtime
current_time = order.date_approve
- delta_time = current_time + timedelta(days=2)
+ delta_time = current_time + timedelta(days=leadtime)
delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S')
order.date_planned = delta_time
else:
@@ -209,6 +269,8 @@ class PurchaseOrder(models.Model):
self.order_line.unlink()
for order_line in self.sale_order_id.order_line:
+ if order_line.vendor_id != self.partner_id:
+ continue
if order_line.product_id.id and order_line.product_id.id not in products_exception:
qty_available = order_line.product_id.qty_onhand_bandengan + order_line.product_id.qty_incoming_bandengan - order_line.product_id.outgoing_qty
@@ -249,8 +311,16 @@ class PurchaseOrder(models.Model):
amount += line.price_total
order.delivery_amount = amount
+ def date_deadline_ref_date_planned(self):
+ for picking in self.picking_ids:
+ if picking.state in ['done', 'cancel']:
+ continue
+ picking.scheduled_date = self.date_planned
+ picking.date_deadline = self.date_planned
+
def button_confirm(self):
res = super(PurchaseOrder, self).button_confirm()
+ current_time = datetime.now()
if self.total_percent_margin < self.total_so_percent_margin and not self.env.user.is_purchasing_manager and not self.env.user.is_leader:
raise UserError("Beda Margin dengan Sales, harus approval Manager")
@@ -268,6 +338,11 @@ class PurchaseOrder(models.Model):
if send_email:
self._send_mail()
+
+ if self.revisi_po:
+ delta_time = current_time - timedelta(days=2)
+ delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S')
+ self.date_approve = delta_time
self.approval_status = 'approved'
self.po_status = 'menunggu'
@@ -275,10 +350,11 @@ class PurchaseOrder(models.Model):
self.calculate_line_no()
# override date planned added with two days
- current_time = datetime.now()
- delta_time = current_time + timedelta(days=2)
+ leadtime = self.partner_id.leadtime
+ delta_time = current_time + timedelta(days=leadtime)
delta_time = delta_time.strftime('%Y-%m-%d %H:%M:%S')
self.date_planned = delta_time
+ self.date_deadline_ref_date_planned()
return res
diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py
index 8d7d818b..c2e7a4c7 100755
--- a/indoteknik_custom/models/purchase_order_line.py
+++ b/indoteknik_custom/models/purchase_order_line.py
@@ -2,6 +2,7 @@ from odoo import fields, models, api, _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
import logging
+from datetime import datetime
_logger = logging.getLogger(__name__)
@@ -35,6 +36,46 @@ class PurchaseOrderLine(models.Model):
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')
+
+ def _compute_is_edit_product_qty(self):
+ for line in self:
+
+ if line.order_id.state in ['draft']:
+ is_valid = True
+ else:
+ is_valid = False
+
+ line.is_edit_product_qty = is_valid
+
+ # @api.constrains('product_qty')
+ # def change_qty_po_and_qty_demand(self):
+ # for line in self:
+ # if line.order_id.state in ['draft', 'cancel'] and len(line.order_id.picking_ids) == 0:
+ # continue
+
+ # for stock_picking in line.order_id.picking_ids:
+ # picking = self.env['stock.move'].search([
+ # ('picking_id.purchase_id', '=', line.order_id.id),
+ # ('product_id', '=', line.product_id.id)
+ # ])
+
+ # if picking:
+ # picking.write({
+ # 'product_uom_qty': line.product_qty
+ # })
+
+ def _compute_qty_reserved(self):
+ for line in self:
+ sale_line = self.env['sale.order.line'].search([
+ ('product_id', '=', line.product_id.id),
+ ('order_id', '=', line.order_id.sale_order_id.id)
+ ])
+
+ 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
@@ -73,14 +114,12 @@ class PurchaseOrderLine(models.Model):
def _onchange_product_custom(self):
self._compute_qty_stock()
- # Override method from addons/purchase/models/purchase.py
- @api.onchange('product_qty', 'product_uom')
+ @api.onchange('product_id','product_qty', 'product_uom')
def _onchange_quantity(self):
res = super(PurchaseOrderLine, self)._onchange_quantity()
- # Custom script
purchase_pricelist = self.env['purchase.pricelist'].search([
('product_id', '=', self.product_id.id),
- ('vendor_id', '=', self.partner_id.id)
+ ('vendor_id', '=', self.partner_id.id),
], limit=1)
price_unit = purchase_pricelist.product_price
@@ -91,8 +130,28 @@ class PurchaseOrderLine(models.Model):
], limit=1)
price_unit = product_supplierinfo.price
+ price_unit, taxes = self._get_valid_purchase_price(purchase_pricelist)
+
self.price_unit = price_unit
+ if purchase_pricelist.taxes_product_id or purchase_pricelist.taxes_system_id:
+ self.taxes_id = taxes
+
return res
+
+ def _get_valid_purchase_price(self, purchase_price):
+ price = 0
+ taxes = None
+ human_last_update = purchase_price.human_last_update or datetime.min
+ system_last_update = purchase_price.system_last_update or datetime.min
+
+ if system_last_update > human_last_update:
+ price = purchase_price.system_price
+ taxes = [purchase.taxes_system_id.id for purchase in purchase_price ]
+ else:
+ price = purchase_price.product_price
+ taxes = [purchase.taxes_product_id.id for purchase in purchase_price ]
+
+ return price, taxes
def compute_item_margin(self):
sum_so_margin = sum_sales_price = sum_margin = 0
diff --git a/indoteknik_custom/models/purchase_order_multi_confirm.py b/indoteknik_custom/models/purchase_order_multi_confirm.py
new file mode 100644
index 00000000..ac77ca54
--- /dev/null
+++ b/indoteknik_custom/models/purchase_order_multi_confirm.py
@@ -0,0 +1,22 @@
+from odoo import models, fields
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchaseOrderMultiUpdate(models.TransientModel):
+ _name = 'purchase.order.multi_confirm'
+
+ def save_multi_confirm_po(self):
+ order_ids = self._context['order_ids']
+ purchase = self.env['purchase.order'].browse(order_ids)
+ purchase.action_multi_confirm_po()
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': 'Notification',
+ 'message': 'PO berhasil di Confirm',
+ 'next': {'type': 'ir.actions.act_window_close'},
+ }
+ } \ No newline at end of file
diff --git a/indoteknik_custom/models/purchase_order_multi_update.py b/indoteknik_custom/models/purchase_order_multi_update.py
new file mode 100644
index 00000000..aab46de8
--- /dev/null
+++ b/indoteknik_custom/models/purchase_order_multi_update.py
@@ -0,0 +1,22 @@
+from odoo import models, fields
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchaseOrderMultiUpdate(models.TransientModel):
+ _name = 'purchase.order.multi_update'
+
+ def save_multi_update_paid_status(self):
+ purchase_ids = self._context['purchase_ids']
+ purchase = self.env['purchase.order'].browse(purchase_ids)
+ purchase.action_multi_update_paid_status()
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': 'Notification',
+ 'message': 'Paid Status berhasil diubah',
+ 'next': {'type': 'ir.actions.act_window_close'},
+ }
+ } \ No newline at end of file
diff --git a/indoteknik_custom/models/purchase_pricelist.py b/indoteknik_custom/models/purchase_pricelist.py
index b7c3785a..67b22e4c 100755
--- a/indoteknik_custom/models/purchase_pricelist.py
+++ b/indoteknik_custom/models/purchase_pricelist.py
@@ -11,17 +11,19 @@ class PurchasePricelist(models.Model):
product_id = fields.Many2one('product.product', string="Product", required=True)
vendor_id = fields.Many2one('res.partner', string="Vendor", required=True)
product_price = fields.Float(string='Human Price', required=True)
- system_price = fields.Float(string='System Price', required=True)
+ system_price = fields.Float(string='System Price', readonly=True)
human_last_update = fields.Datetime(string='Human Update')
system_last_update = fields.Datetime(string='System Update')
count_trx_po = fields.Integer(string='Count Trx Product')
count_trx_po_vendor = fields.Integer(string='Count Trx Vendor')
-
+ taxes_product_id = fields.Many2one('account.tax', string='Taxes Human', domain=[('type_tax_use', '=', 'purchase')])
+ taxes_system_id = fields.Many2one('account.tax', string='Taxes System', readonly=True)
+ include_price = fields.Float(string='Final Price', readonly=True)
@api.depends('product_id', 'vendor_id')
def _compute_name(self):
self.name = self.vendor_id.name + ', ' + self.product_id.name
- @api.constrains('product_price','system_price','vendor_id','product_id')
+ @api.constrains('product_price','system_price','vendor_id','product_id','taxes_system_id','taxes_product_id')
def _contrains_product_price(self):
current_time = fields.Datetime.now(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S')
update_by = self._context.get('update_by')
@@ -30,6 +32,41 @@ class PurchasePricelist(models.Model):
else:
self.human_last_update = current_time
+ @api.constrains('system_last_update','system_price','product_price','human_last_update','taxes_system_id','taxes_product_id')
+ def _contrains_include_price(self):
+
+ price_unit, taxes = self._get_valid_price()
+ if price_unit == 0:
+ self.include_price = 0
+ return
+
+ tax_include = taxes.price_include
+ if taxes:
+ if tax_include:
+ price_unit = price_unit
+ else:
+ price_unit = price_unit + (price_unit * 11 / 100)
+ else:
+ price_unit = price_unit + (price_unit * 11 / 100)
+
+ self.include_price = price_unit
+
+ def _get_valid_price(self):
+ purchase_price = self
+ price = 0
+ taxes = None
+ human_last_update = purchase_price.human_last_update or datetime.min
+ system_last_update = purchase_price.system_last_update or datetime.min
+
+ if system_last_update > human_last_update:
+ price = purchase_price.system_price
+ taxes = purchase_price.taxes_system_id
+ else:
+ price = purchase_price.product_price
+ taxes = purchase_price.taxes_product_id
+
+ return price, taxes
+
@api.constrains('vendor_id', 'product_id')
def _check_duplicate_purchase_pricelist(self):
for price in self:
@@ -39,8 +76,8 @@ class PurchasePricelist(models.Model):
]
domain.append(('id', '!=', price.id))
- massage="Product dan vendor yang anda gunakan sudah ada di purchase pricelist"
existing_purchase = self.search(domain, limit=1)
+ massage="Ada duplikat product dan vendor, berikut data yang anda duplikat : \n" + str(existing_purchase.product_id.name) + " - " + str(existing_purchase.vendor_id.name) + " - " + str(existing_purchase.product_price)
if existing_purchase:
raise UserError(massage)
\ No newline at end of file
diff --git a/indoteknik_custom/models/report_logbook_sj.py b/indoteknik_custom/models/report_logbook_sj.py
new file mode 100644
index 00000000..d2008608
--- /dev/null
+++ b/indoteknik_custom/models/report_logbook_sj.py
@@ -0,0 +1,34 @@
+from odoo import models, fields, api
+from odoo.exceptions import UserError
+from pytz import timezone
+from datetime import datetime
+
+class ReportLogbookSJ(models.Model):
+ _name = 'report.logbook.sj'
+
+ name = fields.Char(string='Nomor SJ', default='Logbook SJ')
+ date = fields.Datetime(string='Date')
+ name_picking = fields.Char(string='Picking Name')
+ partner_id = fields.Many2one('res.partner', string='Customer')
+ approve_by_finance = fields.Boolean(string='Approve By Finance')
+ report_logbook_sj_line = fields.One2many(
+ comodel_name='report.logbook.sj.line',
+ inverse_name='report_logbook_sj_id',
+ string='Logbook SJ Line'
+ )
+
+ def approve(self):
+ if self.env.user.is_accounting:
+ self.approve_by_finance = True
+ else:
+ raise UserError('Hanya Accounting yang bisa Approve')
+
+class ReportLogbookSJLine(models.Model):
+ _name = 'report.logbook.sj.line'
+
+ product_id = fields.Many2one(comodel_name='product.product', string='Product')
+ location_id = fields.Many2one(comodel_name='stock.location', string='From')
+ product_uom_qty = fields.Float(string='Reserved')
+ qty_done = fields.Float(string='Done')
+ product_uom_id = fields.Many2one('uom.uom', string='Unit of Measure')
+ report_logbook_sj_id = fields.Many2one('report.logbook.sj', string='Logbook SJ') \ No newline at end of file
diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py
index 7ceff6e5..2b148c96 100644
--- a/indoteknik_custom/models/requisition.py
+++ b/indoteknik_custom/models/requisition.py
@@ -1,6 +1,7 @@
-from odoo import models, fields, api
+from odoo import models, fields, api, _
from odoo.exceptions import UserError
from datetime import datetime
+import math
import logging
_logger = logging.getLogger(__name__)
@@ -28,121 +29,40 @@ class Requisition(models.Model):
result = super(Requisition, self).create(vals)
return result
- # def create_requisition_from_sales_with_price(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')
-
- # 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 = order_line.purchase_price
- # vendor_id = order_line.vendor_id.id
-
- # # get qty available bandengan
- # qty_available = order_line.product_id.qty_onhand_bandengan + order_line.product_id.qty_incoming_bandengan - order_line.product_id.outgoing_qty
- # suggest = 'harus beli'
- # if qty_available > order_line.product_qty:
- # suggest = 'masih cukup'
-
- # 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': 'sales',
- # 'qty_available_store': qty_available,
- # 'suggest': suggest,
- # }])
- # count+=1
- # _logger.info('Create Requisition %s' % order_line.product_id.name)
- # self.notification = "Requisition Created %s lines" % count
-
- # 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
-
- # # get qty available bandengan
- # qty_available = order_line.product_id.qty_onhand_bandengan + order_line.product_id.qty_incoming_bandengan - order_line.product_id.outgoing_qty
- # suggest = 'harus beli'
- # if qty_available > order_line.product_qty:
- # suggest = 'masih cukup'
-
- # 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,
- # 'qty_available_store': qty_available,
- # 'suggest': suggest,
- # }])
- # 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
+ vendor_ids = self.env['requisition.line'].read_group([
+ ('requisition_id', '=', self.id),
+ ('partner_id', '!=', False)
+ ], fields=['partner_id'], groupby=['partner_id'])
+
+ po_ids = []
for vendor in vendor_ids:
- param_header = {
- 'partner_id': vendor['partner_id'][0],
+ result_po = self.create_po_by_vendor(vendor['partner_id'][0])
+ po_ids += result_po
+ return {
+ 'name': _('Purchase Order'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'purchase.order',
+ 'target': 'current',
+ 'type': 'ir.actions.act_window',
+ 'domain': [('id', 'in', po_ids)],
+ }
+
+ def create_po_by_vendor(self, vendor_id):
+ current_time = datetime.now()
+
+ PRODUCT_PER_PO = 20
+
+ requisition_line = self.env['requisition.line']
+
+ param_header = {
+ 'partner_id': vendor_id,
# 'partner_ref': self.sale_order_id.name,
'currency_id': 12,
'user_id': self.env.user.id,
@@ -152,54 +72,131 @@ class Requisition(models.Model):
'sale_order_id': self.sale_order_id.id,
'note_description': 'from Purchase Requisition'
}
- # 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
-
- for product in products_vendors:
- 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 + "/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
-
- # 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'
-
- tax = [22]
+ domain = [
+ ('requisition_id', '=', self.id),
+ ('partner_id', '=', vendor_id),
+ ('qty_purchase', '>', 0)
+ ]
+
+ products_len = requisition_line.search_count(domain)
+ page = math.ceil(products_len / PRODUCT_PER_PO)
+ po_ids = []
+ # i start from zero (0)
+ for i in range(page):
+ new_po = self.env['purchase.order'].create([param_header])
+ new_po.name = new_po.name + "/R/" + str(i + 1)
+ po_ids.append(new_po.id)
+ lines = requisition_line.search(
+ domain,
+ offset=i * PRODUCT_PER_PO,
+ limit=PRODUCT_PER_PO
+ )
+ tax = [22]
+
+ for line in lines:
+ product = line.product_id
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.price_unit,
+ 'order_id' : new_po.id,
+ 'product_id': product.id,
+ 'product_qty': line.qty_purchase,
+ 'product_uom_qty': line.qty_purchase,
+ 'name': product.name,
+ 'price_unit': line.price_unit,
'taxes_id': tax,
- # 'qty_available_store': qty_available,
- # 'suggest': suggest,
}
- 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
+ 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
+ return po_ids
+
+ # 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
+ # po_ids = []
+ # 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,
+ # 'note_description': 'from Purchase Requisition'
+ # }
+ # param_requisition_line = [
+ # ('requisition_id', '=', self.id),
+ # ('partner_id', '=', vendor['partner_id'][0]),
+ # ('qty_purchase', '>', 0)
+ # ]
+ # # new_po = self.env['purchase.order'].create([param_header])
+ # products_vendors = self.env['requisition.line'].search(, order='brand_id')
+ # count = brand_id = 0
+
+ # for product in products_vendors:
+ # if count > 200 or brand_id != product.brand_id.id:
+ # continue
+
+ # 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
+ # }])
+ # po_ids.append(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'
+
+ # tax = [22]
+
+ # param_line = {
+
+ # 'sequence': count,
+ # 'product_id': product.product_id.id,
+ # 'product_qty': product.qty_purchase,
+ # 'product_uom_qty': product.qty_purchase,
+ # 'price_unit': product.price_unit,
+ # 'taxes_id': tax,
+ # # 'qty_available_store': qty_available,
+ # # 'suggest': suggest,
+ # }
+ # new_line = self.env['purchase.order.line'].create([param_line])
+ # if new_po:
+ # new_line.write({
+ # 'order_id': new_po.id,
+ # })
+ # 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
+ # if po_ids:
+ # return {
+ # 'name': _('Purchase Order'),
+ # 'view_mode': 'tree,form',
+ # 'res_model': 'purchase.order',
+ # 'target': 'current',
+ # 'type': 'ir.actions.act_window',
+ # 'domain': [('id', 'in', po_ids)],
+ # }
class RequisitionLine(models.Model):
_name = 'requisition.line'
diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py
index fcde9369..a7302245 100644
--- a/indoteknik_custom/models/res_partner.py
+++ b/indoteknik_custom/models/res_partner.py
@@ -19,6 +19,7 @@ class ResPartner(models.Model):
])
sppkp = fields.Char(string="SPPKP")
counter = fields.Integer(string="Counter", default=0)
+ leadtime = fields.Integer(string="Leadtime", default=0)
digital_invoice_tax = fields.Boolean(string="Digital Invoice & Faktur Pajak")
is_potential = fields.Boolean(string='Potential')
pakta_integritas = fields.Boolean(string='Pakta Integritas')
diff --git a/indoteknik_custom/models/sale_advance_payment_inv.py b/indoteknik_custom/models/sale_advance_payment_inv.py
index dde7ed74..ebbba6b9 100644
--- a/indoteknik_custom/models/sale_advance_payment_inv.py
+++ b/indoteknik_custom/models/sale_advance_payment_inv.py
@@ -16,6 +16,7 @@ class SaleAdvancePaymentInv(models.TransientModel):
'invoice_user_id': order.user_id.id,
'narration': order.note,
'partner_id': parent_id,
+ 'sale_id': order.id,
'fiscal_position_id': (order.fiscal_position_id or order.fiscal_position_id.get_fiscal_position(order.partner_id.id)).id,
'partner_shipping_id': parent_id.id,
'currency_id': order.pricelist_id.currency_id.id,
diff --git a/indoteknik_custom/models/sale_monitoring_detail.py b/indoteknik_custom/models/sale_monitoring_detail.py
index 43b0b063..8c35b1cc 100755
--- a/indoteknik_custom/models/sale_monitoring_detail.py
+++ b/indoteknik_custom/models/sale_monitoring_detail.py
@@ -23,6 +23,17 @@ class SaleMonitoringDetail(models.Model):
status = fields.Char(string="Status")
qty_reserved = fields.Integer(string="Qty Reserved")
note = fields.Char(string="Note")
+ vendor_id = fields.Many2one('res.partner', string='Vendor')
+
+ def _compute_vendor(self):
+ for r in self:
+ sale_lines = self.env['sale.order.line'].search([
+ ('order_id', '=', r.sale_order_id.id),
+ ('product_id', '=', r.product_id.id),
+ ])
+
+ for line in sale_lines:
+ r.vendor_id = line.vendor_id.id
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
@@ -54,7 +65,8 @@ class SaleMonitoringDetail(models.Model):
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_reserved(so.id, sol.product_id) as qty_reserved,
- sol.note_procurement as note
+ sol.note_procurement as note,
+ sol.vendor_id as vendor_id
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 b1a2f674..362ca574 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -1,6 +1,6 @@
from odoo import fields, models, api, _
-from odoo.exceptions import UserError
-from datetime import datetime
+from odoo.exceptions import UserError, ValidationError
+from datetime import datetime, timedelta
import logging, random, string, requests, math, json, re
_logger = logging.getLogger(__name__)
@@ -84,7 +84,21 @@ class SaleOrder(models.Model):
email = fields.Char(string='Email')
picking_iu_id = fields.Many2one('stock.picking', 'Picking IU')
helper_by_id = fields.Many2one('res.users', 'Helper By')
- # picking_ids = fields.Many2many('stock.picking', string='Pickings', compute='_get_pickings', readonly=True, copy=False, search="_search_picking_ids")
+ eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date')
+
+ def _compute_eta_date(self):
+ max_leadtime = 0
+
+ for line in self.order_line:
+ leadtime = line.vendor_id.leadtime
+ max_leadtime = max(max_leadtime, leadtime)
+
+ for rec in self:
+ if rec.date_order and rec.state not in ['cancel', 'draft']:
+ eta_date = datetime.now() + timedelta(days=max_leadtime)
+ rec.eta_date = eta_date
+ else:
+ rec.eta_date = False
def _prepare_invoice(self):
"""
@@ -109,6 +123,7 @@ class SaleOrder(models.Model):
'medium_id': self.medium_id.id,
'source_id': self.source_id.id,
'user_id': self.user_id.id,
+ 'sale_id': self.id,
'invoice_user_id': self.user_id.id,
'team_id': self.team_id.id,
'partner_id': parent_id.id,
@@ -131,7 +146,7 @@ class SaleOrder(models.Model):
pattern = rf'^{rule_regex}$'
if self.email and not re.match(pattern, self.email):
- raise UserError('Email harus menggunakan karakter @')
+ raise UserError('Email yang anda input kurang valid')
def override_allow_create_invoice(self):
if not self.env.user.is_accounting:
@@ -249,17 +264,18 @@ class SaleOrder(models.Model):
('state', 'in', so_state),
('so_status', '!=', 'terproses'),
])
+
for sale in sales:
- picking_states = {'draft', 'assigned', 'confirmed', 'waiting'}
+ picking_states = ['draft', 'assigned', 'confirmed', 'waiting']
have_outstanding_pick = any(x.state in picking_states for x in sale.picking_ids)
sum_qty_so = sum(so_line.product_uom_qty for so_line in sale.order_line)
sum_qty_ship = sum(so_line.qty_delivered for so_line in sale.order_line)
- if not have_outstanding_pick:
- sale.so_status = 'terproses'
- elif sum_qty_so > sum_qty_ship > 0:
+ if sum_qty_so > sum_qty_ship > 0:
sale.so_status = 'sebagian'
+ elif not have_outstanding_pick:
+ sale.so_status = 'terproses'
else:
sale.so_status = 'menunggu'
@@ -347,9 +363,14 @@ class SaleOrder(models.Model):
minimum_amount = 20000000
for order in self:
order.have_visit_service = self.amount_total > minimum_amount
+
+ def _get_helper_ids(self):
+ helper_ids_str = self.env['ir.config_parameter'].sudo().get_param('sale.order.user_helper_ids')
+ return helper_ids_str.split(', ')
def write(self, values):
- if self.env.user.id in [991, 20, 1180]:
+ helper_ids = self._get_helper_ids()
+ if str(self.env.user.id) in helper_ids:
values['helper_by_id'] = self.env.user.id
return super(SaleOrder, self).write(values)
@@ -367,22 +388,31 @@ class SaleOrder(models.Model):
order.is_warning = False
def _validate_order(self):
+ if self.payment_term_id.id == 31 and self.total_percent_margin < 25:
+ raise UserError("Jika ingin menggunakan Tempo 90 Hari maka margin harus di atas 25%")
+
if self.warehouse_id.id != 8: #GD Bandengan
raise UserError('Gudang harus Bandengan')
+
if self.state not in ['draft', 'sent']:
raise UserError("Status harus draft atau sent")
- if not self._validate_npwp():
- raise UserError("Isi NPWP Dengan Benar!")
+
+ self._validate_npwp()
def _validate_npwp(self):
- pattern = r'^\d{2}\.\d{3}\.\d{3}\.\d{1}-\d{3}\.\d{3}$'
- return re.match(pattern, self.npwp) is not None
+ num_digits = sum(c.isdigit() for c in self.npwp)
+
+ if num_digits < 10:
+ raise UserError("NPWP harus memiliki minimal 10 digit")
+
+ # pattern = r'^\d{10,}$'
+ # return re.match(pattern, self.npwp) is not None
def sale_order_approve(self):
self.check_due()
+ self._validate_order()
for order in self:
- order._validate_order()
order.order_line.validate_line()
partner = order.partner_id.parent_id or order.partner_id
diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index eda003c7..62f4a6b4 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -1,5 +1,6 @@
from odoo import fields, models, api, _
from odoo.exceptions import UserError
+from datetime import datetime
class SaleOrderLine(models.Model):
@@ -27,7 +28,19 @@ class SaleOrderLine(models.Model):
note_procurement = fields.Char(string='Note Detail', help="Harap diisi jika ada keterangan tambahan dari Procurement, agar dapat dimonitoring")
vendor_subtotal = fields.Float(string='Vendor Subtotal', compute="_compute_vendor_subtotal")
amount_voucher_disc = fields.Float(string='Voucher Discount')
-
+ qty_reserved = fields.Float(string='Qty Reserved', compute='_compute_qty_reserved')
+
+ def _compute_qty_reserved(self):
+ for line in self:
+ stock_moves = self.env['stock.move.line'].search([
+ ('product_id', '=', line.product_id.id),
+ ('picking_id.sale_id', '=', line.order_id.id),
+ ('picking_id.state', 'not in', ['cancel', 'done']),
+ ])
+
+ reserved_qty = sum(move.product_uom_qty for move in stock_moves)
+ line.qty_reserved = reserved_qty
+
def _compute_vendor_subtotal(self):
for line in self:
if line.purchase_price > 0 and line.product_uom_qty > 0:
@@ -81,17 +94,16 @@ class SaleOrderLine(models.Model):
self.purchase_tax_id = 22
def _get_valid_purchase_price(self, purchase_price):
- p_price = 0
- if purchase_price.system_price > 0 and purchase_price.product_price > 0:
- if purchase_price.human_last_update > purchase_price.system_last_update:
- p_price = purchase_price.product_price
- else:
- p_price = purchase_price.system_price
- elif purchase_price.system_price > 0 and purchase_price.product_price == 0:
- p_price = purchase_price.system_price
- elif purchase_price.system_price == 0 and purchase_price.product_price > 0:
- p_price = purchase_price.product_price
- return p_price
+ price = 0
+ human_last_update = purchase_price.human_last_update or datetime.min
+ system_last_update = purchase_price.system_last_update or datetime.min
+
+ if system_last_update > human_last_update:
+ price = purchase_price.system_price
+ else:
+ price = purchase_price.product_price
+
+ return price or 0
@api.onchange('product_id')
def product_id_change(self):
diff --git a/indoteknik_custom/models/solr/__init__.py b/indoteknik_custom/models/solr/__init__.py
index f2d13116..606c0035 100644
--- a/indoteknik_custom/models/solr/__init__.py
+++ b/indoteknik_custom/models/solr/__init__.py
@@ -8,4 +8,6 @@ from . import website_categories_homepage
from . import x_manufactures
from . import x_banner_banner
from . import product_public_category
-from . import x_banner_category \ No newline at end of file
+from . import x_banner_category
+from . import promotion_program
+from . import promotion_program_line \ No newline at end of file
diff --git a/indoteknik_custom/models/solr/product_product.py b/indoteknik_custom/models/solr/product_product.py
index fe2a08dc..ac41dbff 100644
--- a/indoteknik_custom/models/solr/product_product.py
+++ b/indoteknik_custom/models/solr/product_product.py
@@ -1,6 +1,7 @@
-from odoo import models, fields, api
from datetime import datetime
+from odoo import api, fields, models
+
class ProductProduct(models.Model):
_inherit = 'product.product'
@@ -37,17 +38,23 @@ class ProductProduct(models.Model):
def _sync_variants_to_solr(self):
solr_model = self.env['apache.solr']
+ ir_attachment = self.env['ir.attachment']
for variant in self:
-
category_id = 0
category_name = ''
for category in variant.product_tmpl_id.public_categ_ids:
category_id = category.id
category_name = category.name
- break
document = solr_model.get_doc('variants', variant.id)
+
+ image = ''
+ if variant.image_256:
+ image = ir_attachment.api_image('product.product', 'image_256', variant.id)
+ else:
+ image = ir_attachment.api_image('product.template', 'image_256', variant.product_tmpl_id.id)
+
document.update({
'id': variant.id,
'display_name_s': variant.display_name,
@@ -56,14 +63,14 @@ class ProductProduct(models.Model):
'product_rating_f': variant.product_tmpl_id.virtual_rating,
'product_id_i': variant.id,
'template_id_i': variant.product_tmpl_id.id,
- 'image_s': self.env['ir.attachment'].api_image('product.template', 'image_512', variant.product_tmpl_id.id),
+ "image_s": image,
'stock_total_f': variant.qty_stock_vendor,
'weight_f': variant.product_tmpl_id.weight,
'manufacture_id_i': variant.product_tmpl_id.x_manufacture.id or 0,
'manufacture_name_s': variant.product_tmpl_id.x_manufacture.x_name or '',
'manufacture_name': variant.product_tmpl_id.x_manufacture.x_name or '',
- 'image_promotion_1_s': self.env['ir.attachment'].api_image('x_manufactures', 'image_promotion_1', variant.product_tmpl_id.x_manufacture.id),
- 'image_promotion_2_s': self.env['ir.attachment'].api_image('x_manufactures', 'image_promotion_2', variant.product_tmpl_id.x_manufacture.id),
+ 'image_promotion_1_s': ir_attachment.api_image('x_manufactures', 'image_promotion_1', variant.product_tmpl_id.x_manufacture.id),
+ 'image_promotion_2_s': ir_attachment.api_image('x_manufactures', 'image_promotion_2', variant.product_tmpl_id.x_manufacture.id),
'category_id_i': category_id,
'category_name_s': category_name,
'category_name': category_name,
diff --git a/indoteknik_custom/models/solr/product_template.py b/indoteknik_custom/models/solr/product_template.py
index bba98edc..062f1455 100644
--- a/indoteknik_custom/models/solr/product_template.py
+++ b/indoteknik_custom/models/solr/product_template.py
@@ -1,6 +1,7 @@
-from odoo import models, fields, api
from datetime import datetime
+from odoo import api, fields, models
+
class ProductTemplate(models.Model):
_inherit = "product.template"
@@ -70,7 +71,7 @@ class ProductTemplate(models.Model):
"default_code_s": template.default_code or '',
"product_rating_f": template.virtual_rating,
"product_id_i": template.id,
- "image_s": self.env['ir.attachment'].api_image('product.template', 'image_512', template.id),
+ "image_s": self.env['ir.attachment'].api_image('product.template', 'image_256', template.id),
"variant_total_i": template.product_variant_count,
"stock_total_f": template.qty_stock_vendor,
"weight_f": template.weight,
diff --git a/indoteknik_custom/models/solr/promotion_program.py b/indoteknik_custom/models/solr/promotion_program.py
new file mode 100644
index 00000000..0d417b3e
--- /dev/null
+++ b/indoteknik_custom/models/solr/promotion_program.py
@@ -0,0 +1,68 @@
+from odoo import models, api
+from datetime import datetime
+from pytz import timezone
+from typing import Type
+import pysolr
+
+
+class PromotionProgram(models.Model):
+ _inherit = 'promotion.program'
+ _solr_schema = 'promotion_programs'
+
+ def solr(self) -> Type[pysolr.Solr]:
+ return self.env['apache.solr'].connect(self._solr_schema)
+
+ def _create_solr_queue(self, function_name: str):
+ for rec in self:
+ self.env['apache.solr.queue'].create_unique({
+ 'res_model': self._name,
+ 'res_id': rec.id,
+ 'function_name': function_name
+ })
+
+ def _sync_to_solr(self):
+ ir_attachment = self.env['ir.attachment']
+ solr_model = self.env['apache.solr']
+
+ for rec in self:
+ document = solr_model.get_doc(self._solr_schema, rec.id)
+ document.update({
+ 'id': rec.id,
+ 'name_s': rec.name,
+ 'banner_s': ir_attachment.api_image(self._name, 'banner', rec.id) if rec.banner else '',
+ 'keywords': [x.name for x in rec.keyword_ids],
+ 'line_ids': [x.id for x in rec.program_line],
+ 'start_time_s': self._time_format(rec.start_time),
+ 'end_time_s': self._time_format(rec.end_time),
+ 'applies_to_s': rec.applies_to,
+ 'icon_s': ir_attachment.api_image(self._name, 'icon', rec.id) if rec.icon else '',
+ 'icon_top_s': ir_attachment.api_image(self._name, 'icon_top', rec.id) if rec.icon_top else '',
+ 'icon_bottom_s': ir_attachment.api_image(self._name, 'icon_bottom', rec.id) if rec.icon_bottom else '',
+ })
+
+ self.solr().add([document])
+
+ self.solr().commit()
+
+ def _time_format(self, object) -> str:
+ time = ''
+ tz_jakarta = timezone('Asia/Jakarta')
+ if isinstance(object, datetime):
+ time = object.astimezone(tz_jakarta).strftime("%Y-%m-%d %H:%M:%S")
+ return time
+
+ @api.model
+ def create(self, vals):
+ self._create_solr_queue('_sync_to_solr')
+ return super(PromotionProgram, self).create(vals)
+
+ def write(self, vals):
+ self._create_solr_queue('_sync_to_solr')
+ return super(PromotionProgram, self).write(vals)
+
+ @api.constrains('program_line')
+ def constrains_program_line(self):
+ for rec in self:
+ for line in rec.program_line:
+ line._create_solr_queue('_sync_to_solr')
+
diff --git a/indoteknik_custom/models/solr/promotion_program_line.py b/indoteknik_custom/models/solr/promotion_program_line.py
new file mode 100644
index 00000000..6e182324
--- /dev/null
+++ b/indoteknik_custom/models/solr/promotion_program_line.py
@@ -0,0 +1,69 @@
+from odoo import models, api
+from typing import Type
+import pysolr
+import json
+
+
+class PromotionProgramLine(models.Model):
+ _inherit = 'promotion.program.line'
+ _solr_schema = 'promotion_program_lines'
+
+ def solr(self) -> Type[pysolr.Solr]:
+ return self.env['apache.solr'].connect(self._solr_schema)
+
+ def _create_solr_queue(self, function_name: str):
+ for rec in self:
+ self.env['apache.solr.queue'].create_unique({
+ 'res_model': self._name,
+ 'res_id': rec.id,
+ 'function_name': function_name
+ })
+
+ def _sync_to_solr(self):
+ solr_model = self.env['apache.solr']
+
+ for rec in self:
+ document = solr_model.get_doc(self._solr_schema, rec.id)
+
+ products = [{
+ 'product_id': x.product_id.id,
+ 'qty': x.qty
+ } for x in rec.product_ids]
+
+ free_products = [{
+ 'product_id': x.product_id.id,
+ 'qty': x.qty
+ } for x in rec.free_product_ids]
+
+ promotion_type = rec._res_promotion_type()
+
+ document.update({
+ 'id': rec.id,
+ 'program_id_i': rec.program_id.id,
+ 'name_s': rec.name,
+ 'image_s': self.env['ir.attachment'].api_image(self._name, 'image', rec.id) if rec.image else '',
+ 'type_value_s': promotion_type['value'],
+ 'type_label_s': promotion_type['label'],
+ 'package_limit_i': rec.package_limit,
+ 'package_limit_user_i': rec.package_limit_user,
+ 'package_limit_trx_i': rec.package_limit_trx,
+ 'price_f': rec.price,
+ 'product_ids': [x.product_id.id for x in rec.product_ids],
+ 'products_s': json.dumps(products),
+ 'free_product_ids': [x.product_id.id for x in rec.free_product_ids],
+ 'free_products_s': json.dumps(free_products),
+ 'total_qty_i': sum([x.qty for x in rec.product_ids] + [x.qty for x in rec.free_product_ids]),
+ })
+
+ self.solr().add([document])
+
+ self.solr().commit()
+
+ @api.model
+ def create(self, vals):
+ self._create_solr_queue('_sync_to_solr')
+ return super(PromotionProgramLine, self).create(vals)
+
+ def write(self, vals):
+ self._create_solr_queue('_sync_to_solr')
+ return super(PromotionProgramLine, self).write(vals)
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index a5e533b1..33946f86 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -69,13 +69,27 @@ class StockPicking(models.Model):
note_logistic = fields.Selection([
('hold', 'Hold by Sales'),
('not_paid', 'Customer belum bayar'),
- ('partial', 'Kirim Parsial')
+ ('partial', 'Kirim Parsial'),
+ ('not_complete', 'Belum Lengkap'),
+ ('indent', 'Indent')
], string='Note Logistic', help='jika field ini diisi maka tidak akan dihitung ke lead time')
waybill_id = fields.One2many(comodel_name='airway.bill', inverse_name='do_id', string='Airway Bill')
purchase_representative_id = fields.Many2one('res.users', related='move_lines.purchase_line_id.order_id.user_id', string="Purchase Representative", readonly=True)
carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method')
shipping_status = fields.Char(string='Shipping Status', compute="_compute_shipping_status")
date_reserved = fields.Datetime(string="Date Reserved", help='Tanggal ter-reserved semua barang nya')
+ status_printed = fields.Selection([
+ ('not_printed', 'Belum Print'),
+ ('printed', 'Printed')
+ ], string='Printed?', copy=False)
+ date_unreserve = fields.Datetime(string="Date Unreserved", copy=False, tracking=True)
+ date_availability = fields.Datetime(string="Date Availability", copy=False, tracking=True)
+
+ def do_unreserve(self):
+ res = super(StockPicking, self).do_unreserve()
+ current_time = datetime.datetime.utcnow()
+ self.date_unreserve = current_time
+ return res
def _compute_shipping_status(self):
for rec in self:
@@ -200,7 +214,9 @@ class StockPicking(models.Model):
def action_assign(self):
res = super(StockPicking, self).action_assign()
+ current_time = datetime.datetime.utcnow()
self.real_shipping_id = self.sale_id.real_shipping_id
+ self.date_availability = current_time
return res
def ask_approval(self):
@@ -281,6 +297,23 @@ class StockPicking(models.Model):
self.is_internal_use = self.picking_type_id.is_internal_use
return
+ def validation_minus_onhand_quantity(self):
+ bu_location_id = 49
+ for line in self.move_line_ids_without_package:
+ quant = self.env['stock.quant'].search([
+ ('product_id', '=', line.product_id.id),
+ ('location_id', '=', bu_location_id),
+ ])
+
+ if (
+ self.picking_type_id.id == 29
+ and quant
+ and line.location_id.id == bu_location_id
+ and quant.inventory_quantity < line.product_uom_qty
+ ):
+ raise UserError('Quantity reserved lebih besar dari quantity onhand di product')
+
+
def button_validate(self):
if self._name != 'stock.picking':
return super(StockPicking, self).button_validate()
@@ -314,6 +347,8 @@ class StockPicking(models.Model):
if not self.date_reserved:
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.date_reserved = current_time
+
+ self.validation_minus_onhand_quantity()
res = super(StockPicking, self).button_validate()
self.calculate_line_no()
diff --git a/indoteknik_custom/models/stock_quant.py b/indoteknik_custom/models/stock_quant.py
new file mode 100644
index 00000000..9affcf4b
--- /dev/null
+++ b/indoteknik_custom/models/stock_quant.py
@@ -0,0 +1,8 @@
+import logging
+
+from odoo import _, api, fields, models
+from odoo.exceptions import UserError
+class StockQuant(models.Model):
+ _inherit = 'stock.quant'
+
+ \ No newline at end of file
diff --git a/indoteknik_custom/models/stock_warehouse_orderpoint.py b/indoteknik_custom/models/stock_warehouse_orderpoint.py
index 277c8dc3..26427c2b 100644
--- a/indoteknik_custom/models/stock_warehouse_orderpoint.py
+++ b/indoteknik_custom/models/stock_warehouse_orderpoint.py
@@ -8,3 +8,6 @@ class StockWarehouseOrderpoint(models.Model):
def _compute_responsible(self):
for stock in self:
stock.responsible_id = stock.product_id.x_manufacture.user_id
+
+ def _get_orderpoint_action(self):
+ return \ No newline at end of file
diff --git a/indoteknik_custom/models/uangmuka_penjualan.py b/indoteknik_custom/models/uangmuka_penjualan.py
index 93a33b52..65f5361b 100644
--- a/indoteknik_custom/models/uangmuka_penjualan.py
+++ b/indoteknik_custom/models/uangmuka_penjualan.py
@@ -48,7 +48,8 @@ class UangmukaPenjualan(models.TransientModel):
param_header = {
'ref': ref_label,
'date': current_time,
- 'journal_id': 11
+ 'journal_id': 11,
+ 'sale_id': order.id,
}
account_move = request.env['account.move'].create([param_header])
diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py
index bbc14c88..eaa5f009 100644
--- a/indoteknik_custom/models/website_user_cart.py
+++ b/indoteknik_custom/models/website_user_cart.py
@@ -51,7 +51,9 @@ class WebsiteUserCart(models.Model):
return res
def get_products(self):
- return [x.get_product() for x in self]
+ products = [x.get_product() for x in self]
+
+ return products
def get_product_by_user(self, user_id, selected=False, source=False):
user_id = int(user_id)
@@ -75,8 +77,20 @@ class WebsiteUserCart(models.Model):
def get_user_checkout(self, user_id, voucher=False, source=False):
products = self.get_product_by_user(user_id=user_id, selected=True, source=source)
- total_purchase = sum(x['price']['price'] * x['quantity'] for x in products)
- total_discount = sum((x['price']['price'] - x['price']['price_discount']) * x['quantity'] for x in products)
+
+ total_purchase = 0
+ total_discount = 0
+ for product in products:
+ if product['cart_type'] == 'promotion':
+ price = product['package_price'] * product['quantity']
+ else:
+ price = product['price']['price'] * product['quantity']
+
+ discount_price = price - product['price']['price_discount'] * product['quantity']
+
+ total_purchase += price
+ total_discount += discount_price
+
subtotal = total_purchase - total_discount
discount_voucher = 0
if voucher:
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index dae13a5d..a8888b64 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -92,4 +92,14 @@ access_v_sales_outstanding,access.v.sales.outstanding,model_v_sales_outstanding,
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 \ No newline at end of file
+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
+access_invoice_reklas_penjualan_line,access.invoice.reklas.penjualan.line,model_invoice_reklas_penjualan_line,,1,1,1,1
+access_purchase_order_multi_confirm,access.purchase.order.multi_confirm,model_purchase_order_multi_confirm,,1,1,1,1
+access_po_multi_cancel,access.po.multi.cancel,model_po_multi_cancel,,1,1,1,1
+access_logbook_sj,access.logbook.sj,model_logbook_sj,,1,1,1,1
+access_logbook_sj_line,access.logbook.sj.line,model_logbook_sj_line,,1,1,1,1
+access_report_logbook_sj,access.report.logbook.sj,model_report_logbook_sj,,1,1,1,1
+access_report_logbook_sj_line,access.report.logbook.sj.line,model_report_logbook_sj_line,,1,1,1,1
diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml
index 7995b83b..97951b95 100644
--- a/indoteknik_custom/views/account_move.xml
+++ b/indoteknik_custom/views/account_move.xml
@@ -15,14 +15,23 @@
</field>
<field name="payment_reference" position="after">
<field name="date_completed" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/>
+ <field name="reklas_id" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/>
+ </field>
+ <field name="invoice_date" position="after">
+ <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/>
+ </field>
+ <field name="ref" position="after">
+ <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'entry')]}"/>
</field>
<field name="efaktur_document" position="before">
<field name="no_faktur_pajak" attrs="{'invisible': [('move_type', '!=', 'in_invoice')]}"/>
+ <field name="date_efaktur_upload" readonly="1"/>
</field>
<field name="efaktur_document" position="attributes">
<attribute name="widget">pdf_viewer</attribute>
</field>
<field name="invoice_user_id" position="after">
+ <field name="invoice_origin"/>
<field name="date_kirim_tukar_faktur"/>
<field name="shipper_faktur_id"/>
<field name="resi_tukar_faktur"/>
@@ -55,7 +64,9 @@
<attribute name="decoration-danger">invoice_day_to_due &lt; 0</attribute>
</field>
<field name="invoice_date_due" position="after">
- <field name="invoice_day_to_due" attrs="{'invisible': [['payment_state', 'in', ('paid', 'in_payment', 'reversed')]]}"/>
+ <field name="new_due_date" optional="hide"/>
+ <field name="invoice_day_to_due" attrs="{'invisible': [['payment_state', 'in', ('paid', 'in_payment', 'reversed')]]}" optional="hide"/>
+ <field name="new_invoice_day_to_due" attrs="{'invisible': [['payment_state', 'in', ('paid', 'in_payment', 'reversed')]]}" optional="hide"/>
<field name="mark_upload_efaktur" optional="hide" widget="badge"
decoration-danger="mark_upload_efaktur == 'belum_upload'"
decoration-success="mark_upload_efaktur == 'sudah_upload'" />
@@ -108,5 +119,13 @@
<field name="state">code</field>
<field name="code">action = records.open_form_multi_update()</field>
</record>
+
+ <record id="account_move_multi_create_reklas_ir_actions_server" model="ir.actions.server">
+ <field name="name">Create Reklas Penjualan</field>
+ <field name="model_id" ref="account.model_account_move"/>
+ <field name="binding_model_id" ref="account.model_account_move"/>
+ <field name="state">code</field>
+ <field name="code">action = records.open_form_multi_create_reklas_penjualan()</field>
+ </record>
</data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/automatic_purchase.xml b/indoteknik_custom/views/automatic_purchase.xml
index 244d1caa..8c381844 100644
--- a/indoteknik_custom/views/automatic_purchase.xml
+++ b/indoteknik_custom/views/automatic_purchase.xml
@@ -24,8 +24,10 @@
<tree editable="bottom">
<field name="brand_id"/>
<field name="product_id"/>
- <field name="qty_min" optional="hide"/>
- <field name="qty_max" optional="hide"/>
+ <field name="taxes_id"/>
+ <field name="qty_purchase"/>
+ <field name="qty_min"/>
+ <field name="qty_max"/>
<field name="qty_available"/>
<field name="qty_purchase"/>
<field name="partner_id"/>
diff --git a/indoteknik_custom/views/invoice_reklas_penjualan.xml b/indoteknik_custom/views/invoice_reklas_penjualan.xml
new file mode 100644
index 00000000..9c875cca
--- /dev/null
+++ b/indoteknik_custom/views/invoice_reklas_penjualan.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="view_invoice_reklas_penjualan" model="ir.ui.view">
+ <field name="name">Invoice Reklas Penjualan</field>
+ <field name="model">invoice.reklas.penjualan</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <field name="name" invisible="1"/>
+ <field
+ name="invoice_reklas_line"
+ mode="tree"
+ >
+ <tree create="0" editable="bottom">
+ <field name="name" readonly="1"/>
+ <field name="partner_id" readonly="1"/>
+ <field name="pay_amt" required="1"/>
+ <field name="reklas_id" required="1"/>
+ <field name="amount_untaxed_signed" readonly="1"/>
+ <field name="sale_id" readonly="1"/>
+ <field name="amount_total_signed" readonly="1"/>
+ </tree>
+ </field>
+ </sheet>
+ <footer>
+ <button name="create_reklas_penjualan" string="Create Reklas" type="object" default_focus="1" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_view_invoice_reklas_penjualan" model="ir.actions.act_window">
+ <field name="name">Create Reklas Penjualan</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">invoice.reklas.penjualan</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+ </record>
+</odoo>
diff --git a/indoteknik_custom/views/logbook_sj.xml b/indoteknik_custom/views/logbook_sj.xml
new file mode 100644
index 00000000..9eb9aa12
--- /dev/null
+++ b/indoteknik_custom/views/logbook_sj.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+ <record id="view_logbook_sj_form" model="ir.ui.view">
+ <field name="name">Logbook SJ</field>
+ <field name="model">logbook.sj</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <field name="name" invisible="1"/>
+ <field
+ name="logbook_sj_line"
+ mode="tree"
+ >
+ <tree editable="bottom">
+ <control>
+ <create name="add_logbook_sj_line_control" string="Add a logbook"/>
+ </control>
+ <field name="name" required="1"/>
+ <field name="driver_id" readonly="1"/>
+ <field name="departure_date" readonly="1"/>
+ <field name="arrival_date" readonly="1"/>
+ <field name="carrier_id" readonly="1"/>
+ <field name="tracking_no" readonly="1"/>
+ <field name="partner_id" readonly="1"/>
+ </tree>
+ </field>
+ </sheet>
+ <footer>
+ <button name="create_logbook_sj" string="Submit" type="object" default_focus="1" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_logbook_sj" model="ir.actions.act_window">
+ <field name="name">Logbook SJ</field>
+ <field name="res_model">logbook.sj</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_logbook_sj_form"/>
+ <field name="target">new</field>
+ </record>
+
+ <menuitem
+ action="action_logbook_sj"
+ id="logbook_sj"
+ parent="stock.menu_stock_warehouse_mgmt"
+ name="Logbook SJ"
+ sequence="1"
+ />
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/po_multi_cancel.xml b/indoteknik_custom/views/po_multi_cancel.xml
new file mode 100644
index 00000000..c17fc5a7
--- /dev/null
+++ b/indoteknik_custom/views/po_multi_cancel.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <data>
+ <record id="view_po_multi_cancel_form" model="ir.ui.view">
+ <field name="name">PO Multi Cancel</field>
+ <field name="model">po.multi.cancel</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <group>
+ <span>Apakah Anda Yakin Ingin Mengubah Status Menjadi Cancel?</span>
+ </group>
+ </sheet>
+ <footer>
+ <button name="save_multi_cancel_po" string="Update" type="object" default_focus="1" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_po_multi_cancel" model="ir.actions.act_window">
+ <field name="name">PO Multi Cancel</field>
+ <field name="res_model">po.multi.cancel</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_po_multi_cancel_form"/>
+ <field name="target">new</field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/product_product.xml b/indoteknik_custom/views/product_product.xml
index 8194f92c..c06cc5f1 100644
--- a/indoteknik_custom/views/product_product.xml
+++ b/indoteknik_custom/views/product_product.xml
@@ -10,6 +10,13 @@
<field name="outgoing_qty"/>
<field name="incoming_qty"/>
</field>
+ <field name="virtual_available" position="after">
+ <field name="qty_onhand_bandengan" optional="hide"/>
+ <field name="qty_incoming_bandengan" optional="hide"/>
+ <field name="qty_outgoing_bandengan" optional="hide"/>
+ <field name="qty_available_bandengan" optional="hide"/>
+ <field name="qty_free_bandengan" optional="hide"/>
+ </field>
</field>
</record>
diff --git a/indoteknik_custom/views/promotion/promotion_program.xml b/indoteknik_custom/views/promotion/promotion_program.xml
index f8432d8a..724f80c7 100644
--- a/indoteknik_custom/views/promotion/promotion_program.xml
+++ b/indoteknik_custom/views/promotion/promotion_program.xml
@@ -22,6 +22,7 @@
<group>
<field name="name" />
<field name="banner" widget="image" height="160" />
+ <field name="image" widget="image" height="160" />
<field name="keyword_ids" widget="many2many_tags" />
</group>
<group>
diff --git a/indoteknik_custom/views/promotion/promotion_program_line.xml b/indoteknik_custom/views/promotion/promotion_program_line.xml
index db6d5252..346a08c9 100644
--- a/indoteknik_custom/views/promotion/promotion_program_line.xml
+++ b/indoteknik_custom/views/promotion/promotion_program_line.xml
@@ -23,7 +23,6 @@
<group>
<field name="name" />
<field name="promotion_type" />
- <field name="image" widget="image" height="160" />
</group>
<group>
<field name="package_limit" />
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index c118f3fb..3f7ca5a7 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -23,11 +23,17 @@
<button name="indoteknik_custom.action_view_uangmuka_pembelian" string="UangMuka"
type="action" attrs="{'invisible': [('approval_status', '!=', 'approved')]}"/>
</button>
+ <button name="button_unlock" position="after">
+ <button name="delete_line" type="object" string="Delete " states="draft"/>
+ </button>
<field name="date_order" position="before">
<field name="sale_order_id" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/>
<field name="approval_status"/>
<field name="amount_total_without_service"/>
</field>
+ <field name="approval_status" position="after">
+ <field name="revisi_po" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/>
+ </field>
<field name="currency_id" position="after">
<field name="summary_qty_po"/>
<field name="count_line_product"/>
@@ -51,11 +57,14 @@
<attribute name="options">{'no_create': True}</attribute>
</field>
<field name="product_qty" position="before">
+ <field name="is_edit_product_qty" readonly="1" optional="hide"/>
<field name="qty_onhand" readonly="1" optional="hide"/>
<field name="qty_incoming" readonly="1" optional="hide"/>
<field name="qty_outgoing" readonly="1" optional="hide"/>
<field name="qty_available" readonly="1" optional="hide"/>
- <field name="suggest" readonly="1"/>
+ <field name="qty_reserved" readonly="1" optional="hide"/>
+ <field name="suggest" readonly="1" widget="badge" decoration-danger="suggest == 'harus beli'" decoration-success="suggest == 'masih cukup'"/>
+ <!-- <field name="suggest" readonly="1"/> -->
</field>
<field name="price_unit" position="after">
<field name="price_vendor" attrs="{'readonly': 1}" optional="hide"/>
@@ -93,7 +102,7 @@
</xpath>
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_qty']" position="attributes">
- <attribute name="attrs">{'readonly': [], 'required': True}</attribute>
+ <attribute name="attrs">{'readonly': [('is_edit_product_qty', '=', False)], 'required': True}</attribute>
</xpath>
<xpath expr="//form/sheet/notebook/page[@name='purchase_delivery_invoice']" position="before">
@@ -212,4 +221,31 @@
</field>
</record>
</data>
+ <data>
+ <record id="purchase_order_multi_update_ir_actions_server" model="ir.actions.server">
+ <field name="name">Update Paid Status</field>
+ <field name="model_id" ref="purchase.model_purchase_order"/>
+ <field name="binding_model_id" ref="purchase.model_purchase_order"/>
+ <field name="state">code</field>
+ <field name="code">action = records.open_form_multi_update_paid_status()</field>
+ </record>
+ </data>
+ <data>
+ <record id="purchase_order_multi_confirm_ir_actions_server" model="ir.actions.server">
+ <field name="name">Confirm PO</field>
+ <field name="model_id" ref="purchase.model_purchase_order"/>
+ <field name="binding_model_id" ref="purchase.model_purchase_order"/>
+ <field name="state">code</field>
+ <field name="code">action = records.open_form_multi_confirm_po()</field>
+ </record>
+ </data>
+ <data>
+ <record id="purchase_order_multi_cancel_ir_actions_server" model="ir.actions.server">
+ <field name="name">Cancel PO</field>
+ <field name="model_id" ref="purchase.model_purchase_order"/>
+ <field name="binding_model_id" ref="purchase.model_purchase_order"/>
+ <field name="state">code</field>
+ <field name="code">action = records.open_form_multi_cancel()</field>
+ </record>
+ </data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/purchase_order_multi_confirm.xml b/indoteknik_custom/views/purchase_order_multi_confirm.xml
new file mode 100644
index 00000000..0d38be9e
--- /dev/null
+++ b/indoteknik_custom/views/purchase_order_multi_confirm.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <data>
+ <record id="view_purchase_order_multi_confirm_po_form" model="ir.ui.view">
+ <field name="name">Purchase Order Multi Confirm</field>
+ <field name="model">purchase.order.multi_confirm</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <group>
+ <span>Apakah Anda Yakin Ingin Confirm PO Tersebut?</span>
+ </group>
+ </sheet>
+ <footer>
+ <button name="save_multi_confirm_po" string="Confirm PO" type="object" default_focus="1" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_purchase_order_multi_confirm" model="ir.actions.act_window">
+ <field name="name">Purchase Order Multi Confirm</field>
+ <field name="res_model">purchase.order.multi_confirm</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_purchase_order_multi_confirm_po_form"/>
+ <field name="target">new</field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/purchase_order_multi_update.xml b/indoteknik_custom/views/purchase_order_multi_update.xml
new file mode 100644
index 00000000..7cfcd64d
--- /dev/null
+++ b/indoteknik_custom/views/purchase_order_multi_update.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <data>
+ <record id="view_purchase_order_multi_update_paid_form" model="ir.ui.view">
+ <field name="name">Purchase Order Multi Update Paid Status</field>
+ <field name="model">purchase.order.multi_update</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <group>
+ <span>Apakah Anda Yakin Ingin Mengubah Paid Status?</span>
+ </group>
+ </sheet>
+ <footer>
+ <button name="save_multi_update_paid_status" string="Update Paid Status" type="object" default_focus="1" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_purchase_order_multi_update" model="ir.actions.act_window">
+ <field name="name">Purchase Order Multi Update Paid Status</field>
+ <field name="res_model">purchase.order.multi_update</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_purchase_order_multi_update_paid_form"/>
+ <field name="target">new</field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/purchase_pricelist.xml b/indoteknik_custom/views/purchase_pricelist.xml
index f4cd4e78..df379804 100755
--- a/indoteknik_custom/views/purchase_pricelist.xml
+++ b/indoteknik_custom/views/purchase_pricelist.xml
@@ -8,7 +8,10 @@
<field name="product_id"/>
<field name="vendor_id"/>
<field name="product_price"/>
+ <field name="taxes_product_id"/>
<field name="system_price"/>
+ <field name="taxes_system_id"/>
+ <field name="include_price"/>
<field name="human_last_update"/>
<field name="system_last_update"/>
<field name="count_trx_po"/>
@@ -28,7 +31,10 @@
<field name="product_id"/>
<field name="vendor_id" context="{'res_partner_search_mode': 'supplier'}"/>
<field name="product_price"/>
+ <field name="taxes_product_id"/>
<field name="system_price"/>
+ <field name="taxes_system_id"/>
+ <field name="include_price"/>
<field name="human_last_update"/>
<field name="system_last_update"/>
<field name="count_trx_po"/>
diff --git a/indoteknik_custom/views/report_logbook_sj.xml b/indoteknik_custom/views/report_logbook_sj.xml
new file mode 100644
index 00000000..52e00d17
--- /dev/null
+++ b/indoteknik_custom/views/report_logbook_sj.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <record id="report_logbook_sj_tree" model="ir.ui.view">
+ <field name="name">report.logbook.sj.tree</field>
+ <field name="model">report.logbook.sj</field>
+ <field name="arch" type="xml">
+ <tree create="0">
+ <field name="name"/>
+ <field name="date"/>
+ <field name="name_picking"/>
+ <field name="approve_by_finance"/>
+ <field name="partner_id"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="report_logbook_sj_line_tree" model="ir.ui.view">
+ <field name="name">report.logbook.sj.line.tree</field>
+ <field name="model">report.logbook.sj.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="product_id"/>
+ <field name="location_id"/>
+ <field name="product_uom_qty"/>
+ <field name="qty_done"/>
+ <field name="product_uom_id"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="report_logbook_sj_form" model="ir.ui.view">
+ <field name="name">report.logbook.sj.form</field>
+ <field name="model">report.logbook.sj</field>
+ <field name="arch" type="xml">
+ <form>
+ <header>
+ <button name="approve"
+ string="Approve"
+ type="object"
+ />
+ </header>
+ <sheet string="Report logbook SJ">
+ <div class="oe_button_box" name="button_box"/>
+ <group>
+ <group>
+ <field name="name" readonly="1"/>
+ <field name="date" readonly="1"/>
+ </group>
+ <group>
+ <field name="name_picking" readonly="1"/>
+ <field name="partner_id" readonly="1"/>
+ <field name="approve_by_finance" readonly="1"/>
+ </group>
+ </group>
+ <notebook>
+ <page string="Line">
+ <field name="report_logbook_sj_line"/>
+ </page>
+ </notebook>
+ </sheet>
+ </form>
+ </field>
+ </record>
+<!--
+ <record id="view_report_logbook_sj_filter" model="ir.ui.view">
+ <field name="name">report.logbook.sj.list.select</field>
+ <field name="model">report.logbook.sj</field>
+ <field name="priority" eval="15"/>
+ <field name="arch" type="xml">
+ <search string="Search Report Logbook SJ">
+ <field name="number"/>
+ <field name="partner_id"/>
+ <field name="dunning_line" string="Invoice" filter_domain="[('dunning_line.invoice_id', 'ilike', self)]"/>
+ </search>
+ </field>
+ </record> -->
+
+ <record id="report_logbook_sj_action" model="ir.actions.act_window">
+ <field name="name">Report Logbook SJ</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">report.logbook.sj</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem id="menu_report_logbook_sj"
+ name="Report Logbook SJ"
+ action="report_logbook_sj_action"
+ parent="account.menu_finance_reports"
+ sequence="200"/>
+</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml
index 58fff00a..da2dec99 100644
--- a/indoteknik_custom/views/res_partner.xml
+++ b/indoteknik_custom/views/res_partner.xml
@@ -23,6 +23,9 @@
<field name="is_berikat" position="after">
<field name="pakta_integritas"/>
</field>
+ <group name="purchase" position="inside">
+ <field name="leadtime"/>
+ </group>
</field>
</record>
</data>
diff --git a/indoteknik_custom/views/sale_monitoring_detail.xml b/indoteknik_custom/views/sale_monitoring_detail.xml
index ce5b8e9b..5091fb83 100755
--- a/indoteknik_custom/views/sale_monitoring_detail.xml
+++ b/indoteknik_custom/views/sale_monitoring_detail.xml
@@ -10,6 +10,7 @@
<field name="partner_id"/>
<field name="user_id"/>
<field name="product_id"/>
+ <field name="vendor_id"/>
<field name="qty_so"/>
<field name="qty_reserved"/>
<field name="qty_po"/>
@@ -40,6 +41,7 @@
<field name="partner_id"/>
<field name="user_id"/>
<field name="product_id"/>
+ <field name="vendor_id"/>
<field name="status"
widget="badge"
decoration-danger="status == 'Belum/Kurang PO'"
@@ -74,6 +76,7 @@
<field name="user_id"/>
<field name="product_id"/>
<field name="status"/>
+ <field name="vendor_id"/>
</search>
</field>
</record>
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 6b4a4ed7..02dba7d5 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -44,6 +44,9 @@
<field name="user_id" position="after">
<field name="helper_by_id" readonly="1"/>
</field>
+ <field name="tag_ids" position="after">
+ <field name="eta_date" readonly="1"/>
+ </field>
<field name="analytic_account_id" position="after">
<field name="customer_type" required="1"/>
<field name="npwp" placeholder='99.999.999.9-999.999' required="1"/>
@@ -105,6 +108,9 @@
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" position="before">
<field name="line_no" readonly="1" optional="hide"/>
</xpath>
+ <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='qty_delivered']" position="before">
+ <field name="qty_reserved" readonly="1"/>
+ </xpath>
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
</xpath>
diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml
index b0932d5a..979bede3 100644
--- a/indoteknik_custom/views/stock_picking.xml
+++ b/indoteknik_custom/views/stock_picking.xml
@@ -19,6 +19,7 @@
</field>
<field name="partner_id" position="after">
<field name="purchase_representative_id"/>
+ <field name="status_printed"/>
</field>
</field>
</record>
@@ -59,6 +60,7 @@
</field>
<field name="origin" position="after">
<field name="purchase_representative_id"/>
+ <field name="status_printed"/>
<field name="purchase_id"/>
<field name="date_doc_kirim"/>
<field name="summary_qty_operation"/>