summaryrefslogtreecommitdiff
path: root/indoteknik_api
diff options
context:
space:
mode:
authorit-fixcomart <it@fixcomart.co.id>2025-01-15 15:43:42 +0700
committerit-fixcomart <it@fixcomart.co.id>2025-01-15 15:43:42 +0700
commitbe1ee2092fef86e79932206cc48d5a146107ac32 (patch)
treee54a8c6ffda996f212ab1c2d03b27a32bab8ba89 /indoteknik_api
parent8b2897d9c72eb67382221d320d488543aea08323 (diff)
parente4ec9406cd0903db59cfed34781da55a2dba4ca3 (diff)
Merge branch 'odoo-production' into iman/switch-account
Diffstat (limited to 'indoteknik_api')
-rw-r--r--indoteknik_api/controllers/api_v1/flash_sale.py2
-rw-r--r--indoteknik_api/controllers/api_v1/invoice.py25
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py341
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py6
-rw-r--r--indoteknik_api/controllers/api_v1/state.py6
-rw-r--r--indoteknik_api/controllers/api_v1/user.py14
-rw-r--r--indoteknik_api/controllers/controller.py30
-rw-r--r--indoteknik_api/models/__init__.py1
-rw-r--r--indoteknik_api/models/account_move.py5
-rw-r--r--indoteknik_api/models/product_product.py12
-rw-r--r--indoteknik_api/models/product_template.py6
-rw-r--r--indoteknik_api/models/res_partner.py170
-rw-r--r--indoteknik_api/models/res_users.py17
-rw-r--r--indoteknik_api/models/sale_order.py7
14 files changed, 623 insertions, 19 deletions
diff --git a/indoteknik_api/controllers/api_v1/flash_sale.py b/indoteknik_api/controllers/api_v1/flash_sale.py
index 00b1f2e0..6c4ad8c0 100644
--- a/indoteknik_api/controllers/api_v1/flash_sale.py
+++ b/indoteknik_api/controllers/api_v1/flash_sale.py
@@ -20,6 +20,7 @@ class FlashSale(controller.Controller):
query = [
('pricelist_id', '=', pricelist.id)
]
+ formatted_end_date = pricelist.end_date.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' if pricelist.end_date else None
data.append({
'pricelist_id': pricelist.id,
'option': pricelist.flashsale_option,
@@ -29,6 +30,7 @@ class FlashSale(controller.Controller):
'banner_mobile': request.env['ir.attachment'].api_image('product.pricelist', 'banner_mobile', pricelist.id),
'banner_top': request.env['ir.attachment'].api_image('product.pricelist', 'banner_top', pricelist.id),
'duration': pricelist._remaining_time_in_second(),
+ 'end_date': formatted_end_date,
'product_total': request.env['product.pricelist.item'].search_count(query),
})
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/invoice.py b/indoteknik_api/controllers/api_v1/invoice.py
index 4937e8dd..4bd3746c 100644
--- a/indoteknik_api/controllers/api_v1/invoice.py
+++ b/indoteknik_api/controllers/api_v1/invoice.py
@@ -1,7 +1,7 @@
from .. import controller
from odoo import http
from odoo.http import request
-
+from odoo import fields
class Invoice(controller.Controller):
PREFIX = '/api/v1/'
@@ -15,6 +15,7 @@ class Invoice(controller.Controller):
'name': [],
'limit': ['default:0', 'number'],
'offset': ['default:0', 'number'],
+ 'status': [],
})
limit = params['value']['limit']
offset = params['value']['offset']
@@ -27,6 +28,11 @@ class Invoice(controller.Controller):
('state', '=', 'posted'),
('partner_id', 'in', partner_child_ids)
]
+ domain_orders = [
+ ('invoice_status', '!=', 'invoiced'),
+ ('state', '=', 'sale'),
+ ('partner_id', 'in', partner_child_ids)
+ ]
if params['value']['name']:
name = params['value']['name'].replace(' ', '%')
domain += [
@@ -34,10 +40,25 @@ class Invoice(controller.Controller):
('name', 'ilike', '%'+ name +'%'),
('ref', 'ilike', '%'+ name +'%')
]
+ if params['value']['status']:
+ if params['value']['status'] == '1':
+ domain += [('payment_state', '=', 'not_paid'), ('invoice_date_due', '<', fields.Date.today())]
+ elif params['value']['status'] == '2':
+ domain += [('payment_state', '=', 'not_paid'), ('invoice_date_due', '>', fields.Date.today())]
+ elif params['value']['status'] == '3':
+ domain += [('payment_state', '=', 'paid')]
+ elif params['value']['status'] == '0':
+ domain = domain
+ elif params['value']['status'] == '4':
+ domain += [('payment_state', '=', 'not_paid')]
+
invoices = request.env['account.move'].search(domain, offset=offset, limit=limit)
+ sale_orders = request.env['sale.order'].search(domain_orders, offset=offset, limit=limit)
data = {
'invoice_total': request.env['account.move'].search_count(domain),
- 'invoices': [request.env['account.move'].api_v1_single_response(x) for x in invoices]
+ 'invoices': [request.env['account.move'].api_v1_single_response(x) for x in invoices],
+ 'sale_order_total': request.env['sale.order'].search_count(domain_orders),
+ 'sale_orders': [request.env['sale.order'].api_v1_single_response(x) for x in sale_orders]
}
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index f9ec7101..307165b3 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -1,7 +1,10 @@
from .. import controller
from odoo import http
from odoo.http import request
-
+from odoo import fields
+import json
+import base64
+import mimetypes
class Partner(controller.Controller):
_name = 'res.partner'
@@ -135,6 +138,8 @@ class Partner(controller.Controller):
'npwp': [],
'alamat_lengkap_text': [],
'street': [],
+ 'email': [],
+ 'mobile': []
})
id_user = self.get_request_params(kw, {
'id_user': ['number']
@@ -213,6 +218,32 @@ class Partner(controller.Controller):
})
return self.response(data)
+
+ @http.route(prefix + 'partner/payment_term', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_partner_payment_term(self):
+ partner_industry = request.env['account.payment.term'].search([])
+ data = []
+ for industry in partner_industry:
+ if 'tempo' in industry.name.lower():
+ data.append({
+ 'id': industry.id,
+ 'name': industry.name
+ })
+
+ return self.response(data)
+
+ @http.route(prefix + 'partner/detail-tempo/<id>', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_partner_detail_tempo(self, **kw):
+ params = self.get_request_params(kw, {
+ 'id': ['required', 'number']
+ })
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', params['value']['id'])], limit=1)
+ if not pengajuan_tempo:
+ return self.response(code=404, description='pengajuan tempo not found')
+ pengajuan_tempo = request.env['res.partner'].api_single_response(pengajuan_tempo)
+ return self.response(pengajuan_tempo)
@http.route(prefix + 'check/<partner_id>/tempo', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
@@ -227,18 +258,318 @@ class Partner(controller.Controller):
if any(line.days == 0 for line in partner.property_payment_term_id.line_ids):
return self.response(code=402, description='Partner not tempo')
-
- result_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search([('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted')]))
-
+
+ domain_result_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted')]
+ domain_result_jatuh_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted'), ('invoice_date_due', '<', fields.Date.today())]
+ domain_orders = [('partner_id', '=', partner.id), ('invoice_status', '!=', 'invoiced'), ('state', '=', 'sale')]
+
+ result_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search(domain_result_tempo))
+ result_tempo_total = request.env['account.move'].search_count(domain_result_tempo)
+ result_jatuh_tempo = sum(m.amount_total_signed for m in request.env['account.move'].search(domain_result_jatuh_tempo))
+ result_jatuh_tempo_total = request.env['account.move'].search_count(domain_result_jatuh_tempo)
+ orders = request.env['sale.order'].search(domain_orders)
+ orders_total = request.env['sale.order'].search_count(domain_orders)
+
+ total_amount = sum(order.amount_total for order in orders)
+
remaining_limit = partner.blocking_stage - result_tempo if partner.active_limit else None
data = {
'name': partner.name,
+ 'payment_term': partner.property_payment_term_id.name,
'amount_due': result_tempo,
+ 'amount_due_total': result_tempo_total,
+ 'amount_jatuh_tempo_total': result_jatuh_tempo_total,
+ 'amount_jatuh_tempo': result_jatuh_tempo,
+ 'amount_due_sale': total_amount,
+ 'amount_due_sale_total': orders_total,
'remaining_limit': remaining_limit
}
-
+
+ return self.response(data)
+
+ @http.route(prefix + 'check/<partner_id>/tempo_progress', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_check_tempo_partner_progres(self, **kw):
+ partner_id = int(kw.get('partner_id'))
+ partner = request.env['res.partner'].search([('id', '=', partner_id)], limit=1)
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', partner.id)], limit=1)
+ if not pengajuan_tempo:
+ return self.response(code=404, description='Partner not found')
+
+ data = True if pengajuan_tempo.id else False
return self.response(data)
+ @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST'], csrf=False)
+ @controller.Controller.must_authorized()
+ def write_pengajuan_tempo(self, **kw):
+ id = int(kw.get('partner_id'))
+ user_id = int(kw.get('user_id'))
+ tempo_request = True if kw.get('tempo_request') == 'true' else False
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', user_id)], limit=1)
+ user = request.env['res.partner'].search([('id', '=', user_id)], limit=1)
+ company_name = kw.get('name', pengajuan_tempo.name_tempo.name)
+ partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1)
+ user_account = self.get_user_by_email(user.email)
+ params = self.get_request_params(kw, {
+
+ # informasi perusahaan
+ # 'name': ['required', 'alias:name_tempo'],
+ 'industryId': ['alias:industry_id_tempo'],
+ 'street': ['alias:street_tempo'],
+ 'state': ['alias:state_id_tempo'],
+ 'city': ['alias:city_id_tempo'],
+ 'district': ['alias:district_id_tempo'],
+ 'subDistrict': ['alias:subDistrict_id_tempo'],
+ 'zip': ['alias:zip_tempo'],
+ 'mobile': ['alias:mobile_tempo'],
+ 'bankName': ['alias:bank_name_tempo'],
+ 'accountName': ['alias:account_name_tempo'],
+ 'accountNumber': ['alias:account_number_tempo'],
+ 'website': ['alias:website_tempo'],
+ 'estimasi': ['alias:estimasi_tempo'],
+ 'portal': ['alias:portal'],
+ 'bersedia': ['alias:bersedia'],
+ 'tempoDuration': ['alias:tempo_duration'],
+ 'tempoLimit': ['alias:tempo_limit'],
+
+ # informasi perusahaan
+ 'direkturTittle': ['alias:direktur_tittle'],
+ 'direkturName': ['alias:direktur_name'],
+ 'direkturMobile': ['alias:direktur_mobile'],
+ 'direkturEmail': ['alias:direktur_email'],
+ 'purchasingTittle': ['alias:purchasing_tittle'],
+ 'purchasingName': ['alias:purchasing_name'],
+ 'purchasingMobile': ['alias:purchasing_mobile'],
+ 'purchasingEmail': ['alias:purchasing_email'],
+ 'financeTittle': ['alias:finance_tittle'],
+ 'financeName': ['alias:finance_name'],
+ 'financeMobile': ['alias:finance_mobile'],
+ 'financeEmail': ['alias:finance_email'],
+
+ # Pengiriman
+ 'PICTittle': ['alias:pic_tittle'],
+ 'PICName': ['alias:pic_name'],
+ 'streetPengiriman': ['alias:street_pengiriman'],
+ 'statePengiriman': ['alias:state_id_pengiriman'],
+ 'cityPengiriman': ['alias:city_id_pengiriman'],
+ 'districtPengiriman': ['alias:district_id_pengiriman'],
+ 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'],
+ 'zipPengiriman': ['alias:zip_pengiriman'],
+ 'invoicePicTittle': ['alias:invoice_pic_tittle'],
+ 'invoicePic': ['alias:invoice_pic'],
+ 'streetInvoice': ['alias:street_invoice'],
+ 'stateInvoice': ['alias:state_id_invoice'],
+ 'cityInvoice': ['alias:city_id_invoice'],
+ 'districtInvoice': ['alias:district_id_invoice'],
+ 'subDistrictInvoice': ['alias:subDistrict_id_invoice'],
+ 'zipInvoice': ['alias:zip_invoice'],
+ 'isSameAddrees':['alias:is_same_address'],
+ 'isSameAddreesStreet':['alias:is_same_address_street'],
+ })
+
+ # # Konversi nilai 'true' ke boolean True
+ # is_same_address = kw.get('isSameAddrees', 'false').lower() == 'true'
+ # is_same_address_street = kw.get('isSameAddreesStreet', 'false').lower() == 'true'
+ #
+ # # Tambahkan nilai yang dikonversi ke params
+ # if 'isSameAddress' in kw:
+ # params['value']['is_same_address'] = is_same_address
+ # if 'is_same_address_street' in kw:
+ # params['value']['is_same_address_street'] = is_same_address_street
+
+ if not params['valid']:
+ return self.response(code=400, description=params)
+ if params['value']['portal']:
+ if params['value']['portal'] == 'ada':
+ params['value']['portal'] = True
+ else:
+ params['value']['portal'] = False
+ # Filter data baru yang dikirim (non-kosong, boolean False tetap masuk)
+ new_data = {key: value for key, value in params['value'].items() if value != ''}
+
+ if pengajuan_tempo:
+ # Jika pengajuan_tempo sudah ada, hanya write data baru yang non-kosong
+ pengajuan_tempo.write(new_data)
+ else:
+ # Jika belum ada, buat record baru
+ pengajuan_tempo = request.env['user.pengajuan.tempo'].create(new_data)
+ pengajuan_tempo.partner_id = user_id
+
+ if partner_id:
+ pengajuan_tempo.name_tempo = partner_id
+
+ form_supplier_data = kw.get('formSupplier', False)
+
+ if form_supplier_data:
+ try:
+ form_supplier_data = json.loads(form_supplier_data)
+
+ supplier_ids_to_add = []
+ for item in form_supplier_data:
+ supplier_name = item.get("supplier")
+ pic_name = item.get("pic")
+ phone = item.get("telepon")
+ tempo_duration = item.get("durasiTempo")
+ credit_limit = item.get("creditLimit")
+
+ new_data = {
+ 'name_supplier': supplier_name,
+ 'pic_name': pic_name,
+ 'phone': phone,
+ 'tempo_duration': tempo_duration,
+ 'credit_limit': credit_limit,
+ }
+ new_supplier_data = request.env['user.pengajuan.tempo.line'].create(new_data)
+
+ supplier_ids_to_add.append(new_supplier_data.id)
+
+ pengajuan_tempo.write({'supplier_ids': [(6, 0, supplier_ids_to_add)]})
+
+ except json.JSONDecodeError:
+ return http.Response(status=400, json_body={'error': 'Invalid JSON format for formSupplier'})
+ category_produk_ids = kw.get('categoryProduk', False)
+ category_ids = ''
+ if category_produk_ids:
+ category_ids = list(map(int, category_produk_ids.split(',')))
+ pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)]
+
+ tukar_invoice_input = kw.get('tukarInvoiceInput')
+ if tukar_invoice_input:
+ pengajuan_tempo.tukar_invoice = tukar_invoice_input
+
+ tukar_invoice_input_pembayaran = kw.get('tukarInvoiceInputPembayaran')
+ if tukar_invoice_input_pembayaran:
+ pengajuan_tempo.jadwal_bayar = tukar_invoice_input_pembayaran
+
+ dokumen_kirim = [
+ 'Surat Tanda Terima Barang (STTB)',
+ 'Good Receipt (GR)',
+ 'Surat Terima Barang (STB)',
+ 'Lembar Penerimaan Barang (LPB)'
+ ]
+
+ dokumen_kirim_barang_ids = kw.get('dokumenPengiriman')
+ dokumen_kirim_input = kw.get('dokumenKirimInput', '')
+ dokumen_kirim_barang_input = kw.get('dokumenPengirimanInput', '')
+ dokumen_kirim_barang = []
+
+ if dokumen_kirim_barang_ids:
+ dokumen_kirim_ids = list(map(int, dokumen_kirim_barang_ids.split(',')))
+ dokumen_kirim_barang = [dokumen_kirim[i] for i in dokumen_kirim_ids if 0 <= i < len(dokumen_kirim)]
+ if dokumen_kirim_input:
+ input_items = [item.strip() for item in dokumen_kirim_input.split(',')]
+ dokumen_kirim_barang.extend(item for item in input_items if item and item not in dokumen_kirim_barang)
+ pengajuan_tempo.dokumen_kirim_input = dokumen_kirim_input
+ if dokumen_kirim_barang:
+ pengajuan_tempo.dokumen_pengiriman = ', '.join(dokumen_kirim_barang)
+ if dokumen_kirim_barang_input:
+ pengajuan_tempo.dokumen_pengiriman_input = dokumen_kirim_barang_input
+
+ dokumen = [
+ 'Invoice Pembelian',
+ 'Surat Jalan',
+ 'Berita Acara Serah Terima (BAST)',
+ 'Faktur Pajak',
+ 'Good Receipt (GR)'
+ ]
+
+ dokumen_invoice = kw.get('dokumenPengirimanInvoice', '')
+ if dokumen_invoice:
+ pengajuan_tempo.dokumen_invoice = dokumen_invoice
+ user_tempo_request = []
+ if tempo_request:
+ user_tempo_request = request.env['user.pengajuan.tempo.request'].create({
+ 'user_id': id,
+ 'pengajuan_tempo_id': pengajuan_tempo.id,
+ 'user_company_id': partner_id.id,
+ 'tempo_duration': pengajuan_tempo.tempo_duration.id,
+ 'tempo_limit': pengajuan_tempo.tempo_limit,
+ })
+
+ form_dokumen_data = kw.get('formDocs', False)
+ if form_dokumen_data:
+ try:
+ form_dokumen = json.loads(form_dokumen_data)
+
+ for dokumen in form_dokumen:
+ if dokumen['details']['base64'] != '':
+ mimetype, _ = mimetypes.guess_type(dokumen['details']['name'])
+ mimetype = mimetype or 'application/octet-stream'
+ data = base64.b64decode(dokumen['details']['base64'])
+ sppkp_attachment = request.env['ir.attachment'].create({
+ 'name': dokumen['details']['name'],
+ 'type': 'binary',
+ 'datas': base64.b64encode(data),
+ 'res_model': 'user.pengajuan.tempo',
+ 'res_id': pengajuan_tempo.id,
+ 'mimetype': mimetype
+ })
+
+ if dokumen['documentName'] == 'dokumenNib':
+ pengajuan_tempo.dokumen_nib = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSiup':
+ pengajuan_tempo.dokumen_siup = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenTdp':
+ pengajuan_tempo.dokumen_tdp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSkdp':
+ pengajuan_tempo.dokumen_skdp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSkt':
+ pengajuan_tempo.dokumen_skt = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenNpwp':
+ pengajuan_tempo.dokumen_npwp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenSppkp':
+ pengajuan_tempo.dokumen_sppkp = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenAktaPerubahan':
+ pengajuan_tempo.dokumen_akta_perubahan = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenKtpDirut':
+ pengajuan_tempo.dokumen_ktp_dirut = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenAktaPendirian':
+ pengajuan_tempo.dokumen_akta_pendirian = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenLaporanKeuangan':
+ pengajuan_tempo.dokumen_laporan_keuangan = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenFotoKantor':
+ pengajuan_tempo.dokumen_foto_kantor = [(6, 0, [sppkp_attachment.id])]
+
+ elif dokumen['documentName'] == 'dokumenTempatBekerja':
+ pengajuan_tempo.dokumen_tempat_bekerja = [(6, 0, [sppkp_attachment.id])]
+
+ formatted_text = ''.join([' ' + char if char.isupper() and i != 0 else char for i, char in
+ enumerate(dokumen['documentName'])])
+ teks = formatted_text.strip().title()
+ pengajuan_tempo.message_post(body=teks, attachment_ids=[sppkp_attachment.id])
+ if tempo_request:
+ user_tempo_request.message_post(body=teks, attachment_ids=[sppkp_attachment.id])
+
+
+ except json.JSONDecodeError:
+ return http.Response(status=400, json_body={'error': 'Invalid JSON format for formDokumen'})
+
+ if tempo_request:
+ # pengajuan_tempo.user_id = id
+ template = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_request_tempo_review')
+ template.send_mail(pengajuan_tempo.id, force_send=True)
+ template2 = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_new_tempo_to_sales')
+ template2.send_mail(pengajuan_tempo.id, force_send=True)
+ return self.response({
+ 'id': pengajuan_tempo.id,
+ 'user_id': user_id,
+ })
+ def get_user_by_email(self, email):
+ return request.env['res.users'].search([
+ ('login', '=', email),
+ ('active', 'in', [True, False])
+ ]) \ No newline at end of file
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py
index 3e182a2e..8b95ade8 100644
--- a/indoteknik_api/controllers/api_v1/sale_order.py
+++ b/indoteknik_api/controllers/api_v1/sale_order.py
@@ -150,7 +150,8 @@ class SaleOrder(controller.Controller):
def partner_checkout_sale_order_by_id(self, **kw):
params = self.get_request_params(kw, {
'partner_id': ['number'],
- 'id': ['number']
+ 'id': ['number'],
+ 'status': ['boolean']
})
if not params['valid']:
return self.response(code=400, description=params)
@@ -163,6 +164,9 @@ class SaleOrder(controller.Controller):
data = {}
sale_order = request.env['sale.order'].search(domain)
if sale_order:
+ if 'status' in params['value']:
+ sale_order.is_continue_transaction = params['value']['status']
+
if sale_order._requires_approval_margin_leader():
sale_order.approval_status = 'pengajuan2'
elif sale_order._requires_approval_margin_manager():
diff --git a/indoteknik_api/controllers/api_v1/state.py b/indoteknik_api/controllers/api_v1/state.py
index 598ef70b..958359a7 100644
--- a/indoteknik_api/controllers/api_v1/state.py
+++ b/indoteknik_api/controllers/api_v1/state.py
@@ -8,7 +8,11 @@ class District(controller.Controller):
@http.route(prefix + 'state', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
def get_state(self, **kw):
+ tempo = kw.get('tempo')
parameters = []
+ if tempo == 'true':
+ parameters.append(('country_id', '=', 100))
+
name = kw.get('name')
if name:
@@ -18,7 +22,7 @@ class District(controller.Controller):
states = request.env['res.country.state'].search(parameters)
data = []
for state in states:
- data.append({ 'id': state.id, 'name': state.name })
+ data.append({ 'id': state.id, 'name': state.name})
return self.response(data)
diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py
index a94ba1f8..f651d9fa 100644
--- a/indoteknik_api/controllers/api_v1/user.py
+++ b/indoteknik_api/controllers/api_v1/user.py
@@ -403,6 +403,20 @@ class User(controller.Controller):
'user': self.response_with_token(user)
})
+ @http.route(prefix + 'user/<id>/after_request_tempo', auth='public', methods=['PUT', 'OPTIONS'], csrf=False)
+ @controller.Controller.must_authorized()
+ def update_user_tempo_after_request(self, **kw):
+ id = kw.get('id')
+
+ user = request.env['res.users'].search([('id', '=', id)], limit=1)
+ if not user:
+ return self.response(code=404, description='User not found')
+
+
+ return self.response({
+ 'user': self.response_with_token(user)
+ })
+
@http.route(prefix + 'user/<id>/address', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
def get_user_address_by_id(self, **kw):
diff --git a/indoteknik_api/controllers/controller.py b/indoteknik_api/controllers/controller.py
index a34a2688..80f45074 100644
--- a/indoteknik_api/controllers/controller.py
+++ b/indoteknik_api/controllers/controller.py
@@ -4,6 +4,7 @@ import functools
import io
import json
from array import array
+from io import BytesIO
import jwt
from odoo import http
@@ -11,6 +12,8 @@ from odoo.http import request
from odoo.modules import get_module_resource
from odoo.tools.config import config
from PIL import Image
+from PIL.WebPImagePlugin import Image
+from PIL import features
from pytz import timezone
@@ -204,6 +207,8 @@ class Controller(http.Controller):
if not variant:
image = self.add_watermark_to_image(image, ratio, version)
+ # image = self.convert_to_webp(image)
+
response_headers = [
('Content-Type', 'image/jpg'),
('Cache-Control', 'public, max-age=3600')
@@ -214,6 +219,31 @@ class Controller(http.Controller):
response_headers
)
+ def convert_to_webp(self, image_base64):
+ """Convert image from base64 to WebP format and return base64 WebP."""
+ try:
+ print(f"Image base64 length: {len(image_base64)}")
+
+ # Decode Base64 to Bytes
+ image_data = base64.b64decode(image_base64)
+ image = Image.open(BytesIO(image_data))
+
+ if image.format == "PNG" and image.mode != "RGBA":
+ image = image.convert("RGBA")
+
+ # Convert to WebP
+ with BytesIO() as output:
+ image.save(output, format="WEBP", quality=85)
+ webp_data = output.getvalue()
+
+ # Encode back to Base64
+ return base64.b64encode(webp_data).decode('utf-8')
+ except Exception as e:
+ print(f"Error details: {e}")
+ # If conversion fails, return the original image
+ request.env.cr.rollback() # Rollback any transactions
+ return image_base64
+
def add_watermark_to_image(self, image, ratio, version = '1'):
if not image: return ''
diff --git a/indoteknik_api/models/__init__.py b/indoteknik_api/models/__init__.py
index 892d2657..8c85938c 100644
--- a/indoteknik_api/models/__init__.py
+++ b/indoteknik_api/models/__init__.py
@@ -9,3 +9,4 @@ from . import sale_order
from . import x_manufactures
from . import website_content
from . import coupon_program
+from . import res_partner
diff --git a/indoteknik_api/models/account_move.py b/indoteknik_api/models/account_move.py
index 23a7076c..645c157a 100644
--- a/indoteknik_api/models/account_move.py
+++ b/indoteknik_api/models/account_move.py
@@ -7,6 +7,7 @@ class AccountMove(models.Model):
def api_v1_single_response(self, account_move, context=False):
sale_order = self.env['sale.order'].search([('name', '=', account_move.invoice_origin), ('state', '=', 'done')], limit=1)
+ sale_order_v2 = self.env['sale.order'].search([('name', '=', account_move.invoice_origin)],limit=1)
amount_residual = account_move.amount_residual
if sale_order.payment_status == 'settlement' or sale_order.payment_status == 'capture':
amount_residual = 0
@@ -21,6 +22,9 @@ class AccountMove(models.Model):
'amount_residual': amount_residual,
'invoice_date': account_move.invoice_date.strftime('%d/%m/%Y') or '',
'efaktur': True if account_move.efaktur_document else False,
+ 'invoice_date_due': account_move.invoice_date_due.strftime('%d/%m/%Y') or '-',
+ 'sales_order': account_move.invoice_origin,
+ 'sales_order_id': sale_order_v2.id,
}
if isinstance(object, datetime.date):
data['invoice_date'] = account_move.invoice_date.strftime('%d/%m/%Y')
@@ -35,7 +39,6 @@ class AccountMove(models.Model):
'sales': account_move.invoice_user_id.name,
'amount_total': account_move.amount_total,
'amount_residual': amount_residual,
- 'invoice_date_due': account_move.invoice_date_due.strftime('%d/%m/%Y') or '',
'customer': res_users.api_address_response(account_move.partner_id),
'products': [],
}
diff --git a/indoteknik_api/models/product_product.py b/indoteknik_api/models/product_product.py
index f8869c7d..10922186 100644
--- a/indoteknik_api/models/product_product.py
+++ b/indoteknik_api/models/product_product.py
@@ -139,39 +139,39 @@ class ProductProduct(models.Model):
return retValue
def _get_website_price_exclude_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_incl = self._get_website_price_include_tax()
res = price_incl / default_divide_tax
return math.floor(res)
def _v2_get_website_price_exclude_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_incl = self._v2_get_website_price_include_tax()
res = price_incl / default_divide_tax
return math.floor(res)
def _get_website_price_after_disc_and_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_after_disc = self._get_website_price_after_disc()
res = price_after_disc / default_divide_tax
res = math.ceil(res)
return res
def _v2_get_website_price_after_disc_and_tax(self):
- default_divide_tax = float(1.11)
+ default_divide_tax = float(1.12)
price_after_disc = self._v2_get_website_price_after_disc()
res = price_after_disc / default_divide_tax
res = math.ceil(res)
return res
def _get_website_tax(self):
- default_percent_tax = float(11)
+ default_percent_tax = float(12)
price_after_disc = self._get_website_price_after_disc_and_tax()
res = price_after_disc * default_percent_tax / 100
return math.floor(res)
def _v2_get_website_tax(self):
- default_percent_tax = float(11)
+ default_percent_tax = float(12)
price_after_disc = self._v2_get_website_price_after_disc_and_tax()
res = price_after_disc * default_percent_tax / 100
return math.floor(res)
diff --git a/indoteknik_api/models/product_template.py b/indoteknik_api/models/product_template.py
index 75899624..e46e44d3 100644
--- a/indoteknik_api/models/product_template.py
+++ b/indoteknik_api/models/product_template.py
@@ -7,6 +7,11 @@ class ProductTemplate(models.Model):
def api_single_response(self, product_template, with_detail=''):
product_pricelist_default_discount_id = self.env['ir.config_parameter'].get_param('product.pricelist.default_discount_id')
product_pricelist_default_discount_id = int(product_pricelist_default_discount_id)
+ voucher = self.get_voucher_pastihemat(product_template.x_manufacture.id)
+ newVoucherPastiHemat = {"min_purchase": voucher.min_purchase_amount or 0,
+ "discount_type": voucher.discount_type or '',
+ "discount_amount": voucher.discount_amount or 0,
+ "max_discount": voucher.max_discount_amount or 0,}
data = {
'id': product_template.id,
'image': self.env['ir.attachment'].api_image('product.template', 'image_128', product_template.id),
@@ -18,6 +23,7 @@ class ProductTemplate(models.Model):
'weight': product_template.weight,
'manufacture': self.api_manufacture(product_template),
'categories': self.api_categories(product_template),
+ "newVoucherPastiHemat": newVoucherPastiHemat
}
if with_detail != '':
diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py
new file mode 100644
index 00000000..0e09fbc6
--- /dev/null
+++ b/indoteknik_api/models/res_partner.py
@@ -0,0 +1,170 @@
+from odoo import models
+import json
+import base64
+
+class ResPartner(models.Model):
+ _inherit = 'res.partner'
+
+ def api_single_response(self, pengajuan_tempo, with_detail=''):
+ config = self.env['ir.config_parameter']
+
+ partner = pengajuan_tempo.partner_id
+
+ dokumen_kirim = [
+ ['Surat Tanda Terima Barang (STTB)', '0'],
+ ['Good Receipt (GR)', '1'],
+ ['Surat Terima Barang (STB)', '2'],
+ ['Lembar Penerimaan Barang (LPB)', '3']
+ ]
+ dokumen_pengiriman = []
+ if pengajuan_tempo.dokumen_pengiriman :
+ pengajuan_tempo_dokumen_pengiriman = pengajuan_tempo.dokumen_pengiriman
+ mapping_dokumen = {item[0]: item[1] for item in dokumen_kirim}
+ dokumen_pengiriman_list = [dokumen.strip() for dokumen in pengajuan_tempo_dokumen_pengiriman.split(',')]
+ dokumen_pengiriman = [mapping_dokumen.get(dokumen, '4') for dokumen in dokumen_pengiriman_list]
+ data = {
+ 'name' : pengajuan_tempo.name_tempo.name,
+ 'industry_id' : pengajuan_tempo.industry_id_tempo.id,
+ 'street' : pengajuan_tempo.street_tempo,
+ 'state' : pengajuan_tempo.state_id_tempo.id,
+ 'city' : pengajuan_tempo.city_id_tempo.id,
+ 'district' : pengajuan_tempo.district_id_tempo.id,
+ 'subDistrict' : pengajuan_tempo.subDistrict_id_tempo.id,
+ 'zip' : pengajuan_tempo.zip_tempo,
+ 'mobile' : pengajuan_tempo.mobile_tempo,
+ 'bank_name' : pengajuan_tempo.bank_name_tempo,
+ 'account_name' : pengajuan_tempo.account_name_tempo,
+ 'account_number' : pengajuan_tempo.account_number_tempo,
+ 'website' : pengajuan_tempo.website_tempo if pengajuan_tempo.website_tempo else '',
+ 'estimasi' : pengajuan_tempo.estimasi_tempo,
+ 'bersedia' : pengajuan_tempo.bersedia,
+ 'portal' : 'ada' if pengajuan_tempo.portal else 'tidak',
+ 'tempo_duration' : pengajuan_tempo.tempo_duration.id,
+ 'tempo_limit' : pengajuan_tempo.tempo_limit,
+ 'category_produk': ','.join([str(cat.id) for cat in pengajuan_tempo.category_produk_ids]) if pengajuan_tempo.category_produk_ids else '',
+
+ # Kontak Perusahaan
+ 'direktur_tittle': pengajuan_tempo.direktur_tittle if pengajuan_tempo.direktur_tittle else '',
+ 'direktur_name' : pengajuan_tempo.direktur_name if pengajuan_tempo.direktur_name else '',
+ 'direktur_mobile' : pengajuan_tempo.direktur_mobile if pengajuan_tempo.direktur_mobile else '',
+ 'direktur_email' : pengajuan_tempo.direktur_email if pengajuan_tempo.direktur_email else '',
+ 'purchasing_tittle': pengajuan_tempo.purchasing_tittle if pengajuan_tempo.purchasing_tittle else '',
+ 'purchasing_name' : pengajuan_tempo.purchasing_name if pengajuan_tempo.purchasing_name else '',
+ 'purchasing_mobile' : pengajuan_tempo.purchasing_mobile if pengajuan_tempo.purchasing_mobile else '',
+ 'purchasing_email' : pengajuan_tempo.purchasing_email if pengajuan_tempo.purchasing_email else '',
+ 'finance_tittle': pengajuan_tempo.finance_tittle if pengajuan_tempo.finance_tittle else '',
+ 'finance_name' : pengajuan_tempo.finance_name if pengajuan_tempo.finance_name else '',
+ 'finance_mobile' : pengajuan_tempo.finance_mobile if pengajuan_tempo.finance_mobile else '',
+ 'finance_email' : pengajuan_tempo.finance_email if pengajuan_tempo.finance_email else '',
+
+ # Pengiriman
+ 'PIC_tittle' : pengajuan_tempo.pic_tittle if pengajuan_tempo.pic_tittle else '',
+ 'PIC_name' : pengajuan_tempo.pic_name if pengajuan_tempo.pic_name else '',
+ 'street_pengiriman' : pengajuan_tempo.street_pengiriman if pengajuan_tempo.street_pengiriman else '',
+ 'state_pengiriman' : pengajuan_tempo.state_id_pengiriman.id if pengajuan_tempo.state_id_pengiriman else '',
+ 'city_pengiriman' : pengajuan_tempo.city_id_pengiriman.id if pengajuan_tempo.city_id_pengiriman else '',
+ 'district_pengiriman': pengajuan_tempo.district_id_pengiriman.id if pengajuan_tempo.district_id_pengiriman else '',
+ 'subDistrict_pengiriman': pengajuan_tempo.subDistrict_id_pengiriman.id if pengajuan_tempo.subDistrict_id_pengiriman else '',
+ 'zip_pengiriman' : pengajuan_tempo.zip_pengiriman if pengajuan_tempo.zip_pengiriman else '',
+ 'invoice_pic_tittle' : pengajuan_tempo.invoice_pic_tittle if pengajuan_tempo.invoice_pic_tittle else '',
+ 'invoice_pic' : pengajuan_tempo.invoice_pic if pengajuan_tempo.invoice_pic else '',
+ 'street_invoice' : pengajuan_tempo.street_invoice if pengajuan_tempo.street_invoice else '',
+ 'state_invoice' : pengajuan_tempo.state_id_invoice.id if pengajuan_tempo.state_id_invoice else '',
+ 'city_invoice' : pengajuan_tempo.city_id_invoice.id if pengajuan_tempo.city_id_invoice else '',
+ 'district_invoice': pengajuan_tempo.district_id_invoice.id if pengajuan_tempo.district_id_invoice else '',
+ 'subDistrict_invoice': pengajuan_tempo.subDistrict_id_invoice.id if pengajuan_tempo.subDistrict_id_invoice else '',
+ 'zip_invoice': pengajuan_tempo.zip_invoice if pengajuan_tempo.zip_invoice else '',
+ 'tukar_invoice_input' : pengajuan_tempo.tukar_invoice if pengajuan_tempo.tukar_invoice else '',
+ 'tukar_invoice_input_pembayaran' : pengajuan_tempo.jadwal_bayar if pengajuan_tempo.jadwal_bayar else '',
+ 'dokumen_pengiriman' : ','.join(dokumen_pengiriman) if dokumen_pengiriman else '',
+ 'dokumen_pengiriman_input' : pengajuan_tempo.dokumen_pengiriman_input if pengajuan_tempo.dokumen_pengiriman_input else '',
+ 'dokumen_kirim_input' : pengajuan_tempo.dokumen_kirim_input if pengajuan_tempo.dokumen_kirim_input else '',
+ 'dokumen_pengiriman_invoice' : pengajuan_tempo.dokumen_invoice if pengajuan_tempo.dokumen_invoice else '',
+ 'is_same_addrees': pengajuan_tempo.is_same_address if pengajuan_tempo.is_same_address else False,
+ 'is_same_addrees_street': pengajuan_tempo.is_same_address_street if pengajuan_tempo.is_same_address_street else False,
+ 'supplier_ids': [
+ {
+ 'id': supplier.id,
+ 'supplier': supplier.name_supplier,
+ 'telepon': supplier.phone,
+ 'pic': supplier.pic_name,
+ 'credit_limit': supplier.credit_limit,
+ 'durasi_tempo': supplier.tempo_duration
+ }
+ for supplier in pengajuan_tempo.supplier_ids
+ ] if pengajuan_tempo.supplier_ids else '',
+ # Dokumen
+ 'dokumen_npwp':
+ {
+ 'name': pengajuan_tempo.dokumen_npwp.name,
+ 'base64': pengajuan_tempo.dokumen_npwp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_npwp.mimetype,
+ } if pengajuan_tempo.dokumen_npwp else '',
+ 'dokumen_sppkp': {
+ 'name': pengajuan_tempo.dokumen_sppkp.name,
+ 'base64': pengajuan_tempo.dokumen_sppkp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_sppkp.mimetype,
+ } if pengajuan_tempo.dokumen_sppkp else '',
+ 'dokumen_nib':
+ {
+ 'name': pengajuan_tempo.dokumen_nib.name,
+ 'base64': pengajuan_tempo.dokumen_nib.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_nib.mimetype,
+ }if pengajuan_tempo.dokumen_nib else '',
+ 'dokumen_siup':
+ {
+ 'name': pengajuan_tempo.dokumen_siup.name,
+ 'base64': pengajuan_tempo.dokumen_siup.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_siup.mimetype,
+ }if pengajuan_tempo.dokumen_siup else '',
+ 'dokumen_tdp':
+ {
+ 'name': pengajuan_tempo.dokumen_tdp.name,
+ 'base64': pengajuan_tempo.dokumen_tdp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_tdp.mimetype,
+ }if pengajuan_tempo.dokumen_tdp else '',
+ 'dokumen_skdp':
+ {
+ 'name': pengajuan_tempo.dokumen_skdp.name,
+ 'base64': pengajuan_tempo.dokumen_skdp.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_skdp.mimetype,
+ }if pengajuan_tempo.dokumen_skdp else '',
+ 'dokumen_skt':
+ {
+ 'name': pengajuan_tempo.dokumen_skt.name,
+ 'base64': pengajuan_tempo.dokumen_skt.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_skt.mimetype,
+ }if pengajuan_tempo.dokumen_skt else '',
+ 'dokumen_akta_perubahan': {
+ 'name': pengajuan_tempo.dokumen_akta_perubahan.name,
+ 'base64': pengajuan_tempo.dokumen_akta_perubahan.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_akta_perubahan.mimetype,
+ } if pengajuan_tempo.dokumen_akta_perubahan else '',
+ 'dokumen_ktp_dirut': {
+ 'name': pengajuan_tempo.dokumen_ktp_dirut.name,
+ 'base64': pengajuan_tempo.dokumen_ktp_dirut.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_ktp_dirut.mimetype,
+ } if pengajuan_tempo.dokumen_ktp_dirut else '',
+ 'dokumen_akta_pendirian': {
+ 'name': pengajuan_tempo.dokumen_akta_pendirian.name,
+ 'base64': pengajuan_tempo.dokumen_akta_pendirian.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_akta_pendirian.mimetype,
+ } if pengajuan_tempo.dokumen_akta_pendirian else '',
+ 'dokumen_laporan_keuangan': {
+ 'name': pengajuan_tempo.dokumen_laporan_keuangan.name,
+ 'base64': pengajuan_tempo.dokumen_laporan_keuangan.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_laporan_keuangan.mimetype,
+ } if pengajuan_tempo.dokumen_laporan_keuangan else '',
+ 'dokumen_foto_kantor': {
+ 'name': pengajuan_tempo.dokumen_foto_kantor.name,
+ 'base64': pengajuan_tempo.dokumen_foto_kantor.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_foto_kantor.mimetype,
+ } if pengajuan_tempo.dokumen_foto_kantor else '',
+ 'dokumen_tempat_bekerja': {
+ 'name': pengajuan_tempo.dokumen_tempat_bekerja.name,
+ 'base64': pengajuan_tempo.dokumen_tempat_bekerja.datas.decode('utf-8'),
+ 'format': pengajuan_tempo.dokumen_tempat_bekerja.mimetype,
+ } if pengajuan_tempo.dokumen_tempat_bekerja else '',
+ }
+
+ return data \ No newline at end of file
diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py
index 1691acde..713bf50c 100644
--- a/indoteknik_api/models/res_users.py
+++ b/indoteknik_api/models/res_users.py
@@ -1,5 +1,5 @@
from odoo import models
-
+from odoo.http import request
class ResUsers(models.Model):
_inherit = 'res.users'
@@ -14,6 +14,15 @@ class ResUsers(models.Model):
'manager': 2,
'director': 3
}
+ partner_tempo = False
+ is_tempo_request = request.env['user.pengajuan.tempo.request'].search([('user_company_id', '=', main_partner.id)], limit=1)
+ tempo_progres = (
+ 'review' if is_tempo_request.state_tempo in ('draft', 'approval_sales', 'approval_finance') else
+ 'rejected' if is_tempo_request.state_tempo == 'reject' else
+ 'approve' if is_tempo_request.state_tempo == 'approval_director' else ''
+ )
+ if main_partner:
+ partner_tempo = True if 'tempo' in main_partner.get_check_payment_term().lower() else False
data = {
'id': res_user.id,
@@ -32,7 +41,9 @@ class ResUsers(models.Model):
'feature': {
'so_approval': main_partner.use_so_approval,
'only_ready_stock': main_partner.use_only_ready_stock
- }
+ },
+ 'partner_tempo': partner_tempo,
+ 'tempo_progres': tempo_progres
}
return data
@@ -70,7 +81,7 @@ class ResUsers(models.Model):
data['state_id'] = {
'id': user.state_id.id,
'name': user.state_id.name
- } or None
+ } or 0
if user.kota_id:
data['city'] = {
diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py
index 54e1fd40..727379c5 100644
--- a/indoteknik_api/models/sale_order.py
+++ b/indoteknik_api/models/sale_order.py
@@ -39,6 +39,8 @@ class SaleOrder(models.Model):
data['status'] = 'cancel'
if sale_order.state in ['draft', 'sent']:
data['status'] = 'draft'
+ if sale_order.is_continue_transaction:
+ data['status'] = 'waiting'
if sale_order.approval_status in ['pengajuan1', 'pengajuan2']:
data['status'] = 'waiting'
if sale_order.state == 'sale':
@@ -85,6 +87,11 @@ class SaleOrder(models.Model):
}
product['quantity'] = line.product_uom_qty
product['available_quantity'] = line.product_available_quantity
+ for data_v2 in sale_order.fulfillment_line_v2:
+ product_v2 = self.env['product.product'].api_single_response(data_v2.product_id)
+ if product['id'] == product_v2['id']:
+ product['so_qty'] = data_v2.so_qty
+ product['reserved_stock_qty'] = data_v2.reserved_stock_qty
data_with_detail['products'].append(product)
for invoice in sale_order.invoice_ids:
if invoice.state == 'posted':