1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
|
from odoo import models, api, fields
from odoo.exceptions import AccessError, UserError, ValidationError
from datetime import timedelta, date, datetime
from pytz import timezone, utc
import logging
import base64
import PyPDF2
import os
import re
from terbilang import Terbilang
_logger = logging.getLogger(__name__)
class AccountMove(models.Model):
_inherit = 'account.move'
invoice_day_to_due = fields.Integer(string="Day to Due", compute="_compute_invoice_day_to_due")
bill_day_to_due = fields.Date(string="Day to Due", compute="_compute_bill_day_to_due")
date_send_fp = fields.Datetime(string="Tanggal Kirim Faktur Pajak")
last_log_fp = fields.Char(string="Log Terakhir Faktur Pajak")
# use for industry business
date_kirim_tukar_faktur = fields.Date(string='Kirim Faktur')
resi_tukar_faktur = fields.Char(string='Resi Faktur')
date_terima_tukar_faktur = fields.Date(string='Terima Faktur')
payment_schedule = fields.Date(string='Jadwal Pembayaran')
shipper_faktur_id = fields.Many2one('delivery.carrier', string='Shipper Faktur')
due_extension = fields.Integer(string='Due Extension', default=0)
new_due_date = fields.Date(string='New Due')
counter = fields.Integer(string="Counter", default=0)
cost_centre_id = fields.Many2one('cost.centre', string='Cost Centre')
analytic_account_ids = fields.Many2many('account.analytic.account', string='Analytic Account')
due_line = fields.One2many('due.extension.line', 'invoice_id', compute='_compute_due_line', string='Due Extension Lines')
no_faktur_pajak = fields.Char(string='No Faktur Pajak')
date_completed = fields.Datetime(string='Date Completed')
mark_upload_efaktur = fields.Selection([
('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)
real_invoice_id = fields.Many2one(
'res.partner', string='Delivery Invoice Address', readonly=True,
states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]},
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
help="Dipakai untuk alamat tempel")
address_invoice = fields.Char(related='real_invoice_id.street', string='Invoice Address', readonly=True)
bills_efaktur_exporter = fields.Many2one('res.users', string='Efaktur Exporter')
bills_date_efaktur = fields.Datetime(string="eFaktur Exported Date", required=False)
bills_efaktur_document = fields.Binary(string="eFaktur", required=False)
bills_invoice_exporter =fields.Many2one('res.users', string='Invoice Exporter')
bills_date_invoice = fields.Datetime(string="Invoice Exported Date", required=False)
bills_invoice_document = fields.Binary(string="Invoice", required=False)
is_invoice_uploaded = fields.Boolean(string="Is Invoice Uploaded", default=False)
is_efaktur_uploaded = fields.Boolean(string="Is eFaktur Uploaded", default=False)
already_paid = fields.Boolean(string="Sudah Dibayar?", default=False)
delivery_amt_text = fields.Char(string="Delivery Amt Terbilang", compute='compute_delivery_amt_text')
so_shipping_paid_by = fields.Char(string="SO Shipping Paid By", compute='compute_so_shipping_paid_by')
so_shipping_covered_by = fields.Char(string="SO Shipping Covered By", compute='compute_so_shipping_paid_by')
so_delivery_amt = fields.Char(string="SO Delivery Amount", compute='compute_so_shipping_paid_by')
flag_delivery_amt = fields.Boolean(string="Flag Delivery Amount", compute='compute_flag_delivery_amt')
nomor_kwitansi = fields.Char(string="Nomor Kwitansi")
other_subtotal = fields.Float(string="Other Subtotal", compute='compute_other_subtotal')
other_taxes = fields.Float(string="Other Taxes", compute='compute_other_taxes')
is_hr = fields.Boolean(string="Is HR?", default=False)
purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order')
length_of_payment = fields.Integer(string="Length of Payment", compute='compute_length_of_payment')
def compute_length_of_payment(self):
for rec in self:
payment_term = rec.invoice_payment_term_id.line_ids[0].days
terima_faktur = rec.date_terima_tukar_faktur
payment = self.search([('ref', '=', rec.name), ('move_type', '=', 'entry')], limit=1)
if payment and terima_faktur:
date_diff = terima_faktur - payment.date
rec.length_of_payment = date_diff.days + payment_term
else:
rec.length_of_payment = 0
def _update_line_name_from_ref(self):
"""Update all account.move.line name fields with ref from account.move"""
for move in self:
if move.move_type == 'entry' and move.ref and move.line_ids:
for line in move.line_ids:
line.name = move.ref
def compute_other_taxes(self):
for rec in self:
rec.other_taxes = round(rec.other_subtotal * 0.12, 2)
def compute_other_subtotal(self):
for rec in self:
rec.other_subtotal = round(rec.amount_untaxed * (11 / 12))
@api.model
def generate_attachment(self, record):
# Fetch the binary field
file_content = record.efaktur_document
file_name = "efaktur_document_{}.pdf".format(record.id) # Adjust the file extension if necessary
attachment = self.env['ir.attachment'].create({
'name': file_name,
'type': 'binary',
'datas': file_content,
'res_model': record._name,
'res_id': record.id,
})
return attachment
@api.constrains('efaktur_document')
def send_scheduled_email(self):
# Get the records for which emails need to be sent
records = self.search([('id', 'in', self.ids)])
template = self.env.ref('indoteknik_custom.mail_template_efaktur_document')
for record in records:
if record.invoice_payment_term_id.id == 26:
attachment = self.generate_attachment(record)
email_values = {
'attachment_ids': [(4, attachment.id)]
}
template.send_mail(record.id, email_values=email_values, force_send=True)
@api.model
def create(self, vals):
vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0'
result = super(AccountMove, self).create(vals)
# result._update_line_name_from_ref()
return result
def compute_so_shipping_paid_by(self):
for record in self:
record.so_shipping_paid_by = record.sale_id.shipping_paid_by
record.so_shipping_covered_by = record.sale_id.shipping_cost_covered
record.so_delivery_amt = record.sale_id.delivery_amt
def compute_flag_delivery_amt(self):
for record in self:
if record.sale_id.delivery_amt > 0:
record.flag_delivery_amt = True
else:
record.flag_delivery_amt = False
def compute_delivery_amt_text(self):
tb = Terbilang()
for record in self:
res = ''
try:
if record.sale_id.delivery_amt > 0:
tb.parse(int(record.sale_id.delivery_amt))
res = tb.getresult().title()
record.delivery_amt_text = res + ' Rupiah'
except:
record.delivery_amt_text = res
@api.constrains('bills_efaktur_document')
def _constrains_efaktur_document(self):
for move in self:
current_time = datetime.utcnow()
move.bills_date_efaktur = current_time
move.bills_efaktur_exporter = self.env.user.id
move.is_efaktur_uploaded = True
@api.constrains('bills_invoice_document')
def _constrains_invoice_efaktur(self):
for move in self:
current_time = datetime.utcnow()
move.bills_date_invoice = current_time
move.bills_invoice_exporter = self.env.user.id
move.is_invoice_uploaded = True
@api.constrains('partner_id')
def _constrains_real_invoice(self):
for move in self:
move.real_invoice_id = move.sale_id.real_invoice_id
@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:
if move.efaktur_document:
move.mark_upload_efaktur = 'sudah_upload'
else:
move.mark_upload_efaktur = 'belum_upload'
def _compute_due_line(self):
for invoice in self:
invoice.due_line = self.env['due.extension.line'].search([
('invoice_id', '=', invoice.id),
('due_id.approval_status', '=', 'approved')
])
def unlink(self):
res = super(AccountMove, self).unlink()
for rec in self:
if rec.state == 'posted':
raise UserError('Data Hanya Bisa Di Cancel')
return res
def button_cancel(self):
res = super(AccountMove, self).button_cancel()
if self.move_type == 'entry':
po = self.env['purchase.order'].search([
('move_id', 'in', [self.id])
])
for order in po:
if order:
order.is_create_uangmuka = False
if self.id and not self.env.user.is_accounting:
raise UserError('Hanya Accounting yang bisa Cancel')
return res
def button_draft(self):
res = super(AccountMove, self).button_draft()
if not self.env.user.is_accounting:
raise UserError('Hanya Accounting yang bisa Reset to Draft')
for rec in self.line_ids:
if rec.write_date != rec.create_date:
if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm':
raise UserError('Bank Statement di Lock, Minta admin reconcile untuk unlock')
return res
def action_post(self):
if self._name != 'account.move':
return super(AccountMove, self).action_post()
# validation cant qty invoice greater than qty order
if self.move_type == 'out_invoice':
query = ["&",("name","=",self.invoice_origin),"|",("state","=","sale"),("state","=","done")]
sale_order = self.env['sale.order'].search(query, limit=1)
sum_qty_invoice = sum_qty_order = 0
for line in sale_order.order_line:
sum_qty_invoice += line.qty_invoiced
sum_qty_order += line.product_uom_qty
if sum_qty_invoice > sum_qty_order:
raise UserError('Error Qty Invoice akan lebih besar dari Qty Order jika lanjut Posting')
elif self.move_type == 'in_invoice':
query = ["&",("name","=",self.invoice_origin),"|",("state","=","purchase"),("state","=","done")]
purchase_order = self.env['purchase.order'].search(query, limit=1)
sum_qty_invoice = sum_qty_order = 0
for line in purchase_order.order_line:
sum_qty_invoice += line.qty_invoiced
sum_qty_order += line.product_qty
if sum_qty_invoice > sum_qty_order:
raise UserError('Error Qty Invoice akan lebih besar dari Qty Order jika lanjut Posting')
res = super(AccountMove, self).action_post()
# if not self.env.user.is_accounting:
# raise UserError('Hanya Accounting yang bisa Posting')
# if self._name == 'account.move':
for entry in self:
entry.date_completed = datetime.utcnow()
for line in entry.line_ids:
line.date_maturity = entry.date
return res
def button_draft(self):
res = super(AccountMove, self).button_draft()
if not self.env.user.is_accounting:
raise UserError("Hanya Finence yang bisa ubah data")
return res
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:
rec.bill_day_to_due = rec.payment_schedule or rec.invoice_date_due
@api.onchange('date_kirim_tukar_faktur')
def change_date_kirim_tukar_faktur(self):
for invoice in self:
if not invoice.date_kirim_tukar_faktur:
return
tukar_date = invoice.date_kirim_tukar_faktur
term = invoice.invoice_payment_term_id
add_days = 0
for line in term.line_ids:
add_days += line.days
due_date = tukar_date + timedelta(days=add_days)
invoice.invoice_date_due = due_date
@api.constrains('date_terima_tukar_faktur')
def change_date_terima_tukar_faktur(self):
for invoice in self:
if not invoice.date_terima_tukar_faktur:
return
tukar_date = invoice.date_terima_tukar_faktur
term = invoice.invoice_payment_term_id
add_days = 0
for line in term.line_ids:
add_days += line.days
due_date = tukar_date + timedelta(days=add_days)
invoice.invoice_date_due = due_date
def open_form_multi_update(self):
action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_account_move_multi_update')
action['context'] = {
'move_ids': [x.id for x in self]
}
return action
def open_form_multi_update_bills(self):
action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_account_move_multi_update_bills')
action['context'] = {
'move_ids': [x.id for x in self]
}
return action
@api.constrains('efaktur_id', 'ref', 'date', 'journal_id', 'name')
def constrains_edit(self):
for rec in self.line_ids:
if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm':
raise UserError('Bank Statement di Lock, Minta admin reconcile untuk unlock')
# def write(self, vals):
# res = super(AccountMove, self).write(vals)
# for rec in self.line_ids:
# if rec.write_date != rec.create_date:
# if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm':
# raise UserError('Bank Statement di Lock, Minta admin reconcile untuk unlock')
# return res
def validate_faktur_for_export(self):
invoices = self.filtered(lambda inv: not inv.is_efaktur_exported and
inv.state == 'posted' and
inv.move_type == 'out_invoice')
invalid_invoices = self - invoices
if invalid_invoices:
invalid_ids = ", ".join(str(inv.id) for inv in invalid_invoices)
raise UserError((
"Faktur dengan ID berikut tidak valid untuk diekspor: {}.\n"
"Pastikan faktur dalam status 'posted', belum diekspor, dan merupakan 'out_invoice'.".format(invalid_ids)
))
return invoices
def export_faktur_to_xml(self):
valid_invoices = self
# Panggil model coretax.faktur untuk menghasilkan XML
coretax_faktur = self.env['coretax.faktur'].create({})
response = coretax_faktur.export_to_download(invoices=valid_invoices)
current_time = datetime.utcnow()
# Tandai faktur sebagai sudah diekspor
valid_invoices.write({
'is_efaktur_exported': True,
'date_efaktur_exported': current_time, # Set tanggal ekspor
})
return response
|