# -*- coding: utf-8 -*-
from odoo import models, fields, api
import requests
import json
from datetime import date, datetime, time, timedelta
from odoo.exceptions import ValidationError, UserError
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
xendit_api_key = fields.Char(string="API Key", config_parameter="aqn_xendit_ext.xendit_api_key")
xendit_journal_id = fields.Many2one('account.journal', string="Payment Journal", config_parameter="aqn_xendit_ext.xendit_journal_id")
xendit_url_callback = fields.Char(string="URL Callback", config_parameter="aqn_xendit_ext.xendit_url_callback")
def get_xendit_config_api(self, param, check_exist=False):
params = {
'xendit_api_key': self.env['ir.config_parameter'].sudo().get_param('aqn_xendit_ext.xendit_api_key'),
'xendit_journal_id': self.env['ir.config_parameter'].sudo().get_param('aqn_xendit_ext.xendit_journal_id'),
'xendit_url_callback': self.env['ir.config_parameter'].sudo().get_param('aqn_xendit_ext.xendit_url_callback'),
}
if param == 'xendit_api_key':
if not params.get('xendit_api_key') and not check_exist:
raise ValidationError('API Key belum diisi di Settings (Xendit API)')
elif param == 'xendit_journal_id':
if not params.get('xendit_journal_id') and not check_exist:
raise ValidationError('Journal belum diisi di Settings (Xendit API)')
elif param == 'xendit_url_callback':
if not params.get('xendit_url_callback') and not check_exist:
raise ValidationError('URL Callback belum diisi di Settings (Xendit API)')
if param:
if param == 'xendit_journal_id':
return int(params.get(param))
else:
return params.get(param)
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import UserError, RedirectWarning, ValidationError
import requests
import json
import base64
import io
# from xendit import Xendit
import uuid
import time
import xendit
from xendit.apis import InvoiceApi
from xendit.invoice.model.invoice_not_found_error import InvoiceNotFoundError
from xendit.invoice.model.invoice import Invoice
from xendit.invoice.model.bad_request_error import BadRequestError
from xendit.invoice.model.unauthorized_error import UnauthorizedError
from xendit.invoice.model.forbidden_error import ForbiddenError
from xendit.invoice.model.create_invoice_request import CreateInvoiceRequest
from xendit.invoice.model.customer_object import CustomerObject
from xendit.invoice.model.address_object import AddressObject
from xendit.invoice.model.notification_preference import NotificationPreference
from xendit.invoice.model.notification_channel import NotificationChannel
from xendit.invoice.model.invoice_item import InvoiceItem
from xendit.invoice.model.invoice_fee import InvoiceFee
from pprint import pprint
class AccountMove(models.Model):
_inherit = 'account.move'
xendit_payment_link = fields.Char('Payment Link', copy=False)
xendit_invoice_id = fields.Char('Xendit Invoice ID', copy=False)
xendit_payment_token = fields.Char('Xendit Payment Token')
def create_payment(self, move_id):
obj_config = self.env['res.config.settings']
obj_move = self.env['account.move']
obj_pym = self.env['account.payment']
move = obj_move.browse(move_id)
journal_id = obj_config.get_xendit_config_api('xendit_journal_id')
payment = obj_pym.create({
'amount': move.amount_residual, # move.amount_total,
'payment_type': 'inbound',
'ref': move.payment_reference,
'partner_id': move.partner_id.id,
'partner_type': 'customer',
'journal_id': journal_id,
})
payment.action_post()
# Reconcile
ml_to_recon = self.env['account.move.line']
# Jurnal Invoice
if move.line_ids:
for ml in move.line_ids:
if ml.account_id.id == move.partner_id.property_account_receivable_id.id:
ml_to_recon += ml
# Jurnal Payment
if payment.line_ids:
for ml in payment.line_ids:
if ml.account_id.id == payment.destination_account_id.id:
ml_to_recon += ml
if ml_to_recon:
ml_to_recon.reconcile()
def rpc_register_payment(self, move_id=False, token=False):
obj_move = self.env['account.move']
move = obj_move.browse(move_id)
if move_id:
if move.xendit_payment_token != token:
return {
'error': True,
'message': f'Token tidak sesuai',
'data': {}
}
self.create_payment(move_id)
return {
'error': False,
'message': f'Pembayaran invoice {move.name} berhasil ditambahkan',
'data': {}
}
return {
'error': True,
'message': f'Invoice dengan ID {move.id} tidak ditemukan',
'data': {}
}
def remote_xendit_payment_link(self):
obj_config = self.env['res.config.settings']
obj_journal = self.env['account.journal']
api_key = obj_config.get_xendit_config_api('xendit_api_key')
xendit.set_api_key(api_key)
api_client = xendit.ApiClient()
api_instance = InvoiceApi(api_client)
for rec in self:
items = []
for item in rec.invoice_line_ids.filtered(lambda x: x.display_type == 'product'):
items.append(
InvoiceItem(
name=item.name,
price=item.price_unit,
quantity=item.quantity,
# reference_id="reference_id_example",
# url="url_example",
# category=item.product_id.categ_id.name,
),
)
if not rec.partner_id.email:
raise ValidationError(f'Email partner {rec.partner_id.name} belum dilengkapi!')
payment_method = ["CREDIT_CARD"]
if not rec.partner_id.country_id:
raise ValidationError(f"Negara untuk partner {rec.partner_id.name} belum dilengkapi!")
if rec.partner_id.country_id.code == 'ID':
# journal_id = obj_config.get_xendit_config_api('xendit_journal_id')
# journal = obj_journal.browse(journal_id)
# if not journal.xendit_bank_code:
# raise ValidationError(f'Xendit Bank Code belum dilengkapi di jurnal pembayaran {journal.name}')
payment_method = ['BCA', 'BNI', 'BSI', 'BRI', 'MANDIRI', 'PERMATA']
url_callback = obj_config.get_xendit_config_api('xendit_url_callback')
token = str(uuid.uuid4())
duration = 0
aging = rec.invoice_date_due - rec.invoice_date
if aging.days == 0:
duration = 24 * 3600
elif aging.days > 365:
duration = 365 * 24 * 3600
else:
duration = aging.days * 24 * 3600
create_invoice_request = CreateInvoiceRequest(
external_id= rec.name,
amount=rec.amount_residual,
payer_email= rec.partner_id.email,
description= rec.payment_reference,
invoice_duration=str(duration),
# callback_virtual_account_id="callback_virtual_account_id_example",
# should_send_email=True,
customer= CustomerObject(
# id=rec.partner_id.id,
phone_number=rec.partner_id.mobile or rec.partner_id.phone or "",
given_names=rec.partner_id.name,
surname=rec.partner_id.name,
email=rec.partner_id.email,
mobile_number=rec.partner_id.mobile or "",
# customer_id=str(rec.partner_id.id),
addresses=[
AddressObject(
country=rec.partner_id.country_id.name or "",
street_line1=rec.partner_id.street or "",
street_line2=rec.partner_id.street2 or "",
city=rec.partner_id.city or "",
province=rec.partner_id.state_id.name or "",
# state=rec.partner_id.state_id.name,
postal_code=rec.partner_id.zip or "",
),
],
),
customer_notification_preference= NotificationPreference(
invoice_created=[
NotificationChannel("email"),
NotificationChannel("whatsapp"),
],
# invoice_reminder=[
# NotificationChannel("email"),
# ],
# invoice_expired=[
# NotificationChannel("email"),
# ],
invoice_paid=[
NotificationChannel("email"),
NotificationChannel("whatsapp"),
],
),
success_redirect_url=f"{url_callback}?move_id={rec.id}&token={token}",
# failure_redirect_url="failure_redirect_url_example",
payment_methods=payment_method,
# mid_label="mid_label_example",
# should_authenticate_credit_card=True,
currency=rec.currency_id.name,
# reminder_time=3.14,
# local="local_example",
# reminder_time_unit="reminder_time_unit_example",
items=items
# fees=[
# InvoiceFee(
# type="type_example",
# value=3.14,
# ),
# ],
) # CreateInvoiceRequest
# for_user_id = "62efe4c33e45694d63f585f8" # str | Business ID of the sub-account merchant (XP feature)
# example passing only required values which don't have defaults set
try:
# Create an invoice
api_response = api_instance.create_invoice(create_invoice_request)
pprint(api_response)
if api_response.invoice_url:
rec.xendit_payment_link = api_response.invoice_url
rec.xendit_invoice_id = api_response.id
rec.xendit_payment_token = token
except xendit.XenditSdkException as e:
raise ValidationError("Exception when calling InvoiceApi->create_invoice: %s\n" % e)