From 2dbf20b1fd8ace6c6ca7e16db40302b04bff39c9 Mon Sep 17 00:00:00 2001 From: Jason Moore Date: Fri, 8 Nov 2024 15:20:31 +0800 Subject: [PATCH] Implement invoice name override and invoice due date --- ledger/checkout/serializers.py | 1 + ledger/checkout/utils.py | 10 +++ ledger/checkout/views.py | 6 +- ledger/payments/admin.py | 2 +- ledger/payments/invoice/facade.py | 12 ++- .../migrations/0012_auto_20241107_1451.py | 25 ++++++ .../migrations/0013_auto_20241107_1451.py | 20 +++++ .../migrations/0014_auto_20241107_1456.py | 20 +++++ ledger/payments/invoice/models.py | 2 + ledger/payments/pdf.py | 76 ++++++++++++++----- ledgergw/api.py | 18 ++++- 11 files changed, 163 insertions(+), 29 deletions(-) create mode 100644 ledger/payments/invoice/migrations/0012_auto_20241107_1451.py create mode 100644 ledger/payments/invoice/migrations/0013_auto_20241107_1451.py create mode 100644 ledger/payments/invoice/migrations/0014_auto_20241107_1456.py diff --git a/ledger/checkout/serializers.py b/ledger/checkout/serializers.py index 68c1d7bb6..095f04dfa 100644 --- a/ledger/checkout/serializers.py +++ b/ledger/checkout/serializers.py @@ -75,6 +75,7 @@ class CheckoutSerializer(serializers.Serializer): icrn_format = serializers.ChoiceField(choices=['ICRNAMT', 'ICRNDATE', 'ICRNAMTDATE'], default='ICRNAMT') icrn_date = serializers.DateField(required=False, default=None) invoice_text = serializers.CharField(required=False, default=None) + invoice_name = serializers.CharField(required=False, default=None) check_url = serializers.URLField(required=False, default=None) amount_override=serializers.FloatField(required=False, default=None) session_type = serializers.ChoiceField(choices=['standard', 'ledger_api'], default='standard') diff --git a/ledger/checkout/utils.py b/ledger/checkout/utils.py index 51694bd8c..efa15ad58 100755 --- a/ledger/checkout/utils.py +++ b/ledger/checkout/utils.py @@ -216,6 +216,7 @@ def create_checkout_session(request, parameters): session_data.icrn_using(serializer.validated_data['icrn_format']) session_data.bpay_by(serializer.validated_data['icrn_date']) session_data.set_invoice_text(serializer.validated_data['invoice_text']) + session_data.set_invoice_name(serializer.validated_data['invoice_name']) session_data.set_last_check(serializer.validated_data['check_url']) session_data.set_amount_override(serializer.validated_data['amount_override']) @@ -376,6 +377,15 @@ def set_invoice_text(self,text): def get_invoice_text(self): return self._get('ledger','invoice_text') + + # Invoice Name Override Account Name + # ========================== + def set_invoice_name(self,text): + self._set('ledger','invoice_name',text) + + def get_invoice_name(self): + return self._get('ledger','invoice_name') + # Last check url per system # ========================== def set_last_check(self,text): diff --git a/ledger/checkout/views.py b/ledger/checkout/views.py index e2841e9a3..f11cbf1fe 100755 --- a/ledger/checkout/views.py +++ b/ledger/checkout/views.py @@ -362,7 +362,8 @@ def doInvoice(self,order_number,total,**kwargs): crn_string, system, self.checkout_session.get_invoice_text() if self.checkout_session.get_invoice_text() else '', - self.checkout_session.payment_method() if self.checkout_session.payment_method() else None + self.checkout_session.payment_method() if self.checkout_session.payment_method() else None, + self.checkout_session.get_invoice_name() if self.checkout_session.get_invoice_name() else '' ) self.createInvoiceLinks(invoice) return invoice @@ -375,7 +376,8 @@ def doInvoice(self,order_number,total,**kwargs): icrn_format, system, self.checkout_session.get_invoice_text() if self.checkout_session.get_invoice_text() else '', - self.checkout_session.payment_method() if self.checkout_session.payment_method() else None + self.checkout_session.payment_method() if self.checkout_session.payment_method() else None, + self.checkout_session.get_invoice_name() if self.checkout_session.get_invoice_name() else '' ) self.createInvoiceLinks(invoice) return invoice diff --git a/ledger/payments/admin.py b/ledger/payments/admin.py index cf1728452..2655fcdcd 100755 --- a/ledger/payments/admin.py +++ b/ledger/payments/admin.py @@ -26,7 +26,7 @@ class CashAdmin(admin.ModelAdmin): @admin.register(models.Invoice) class InvoiceAdmin(admin.ModelAdmin): - list_display = ('reference','oracle_invoice_number','order','payment_status','settlement_date','amount', 'system','created' ) + list_display = ('reference','oracle_invoice_number','order','payment_status','invoice_name','settlement_date','due_date','amount', 'system','created' ) search_fields = ('reference',) list_filter = ('system'), raw_id_fields = ('previous_invoice','oracle_invoice_file') diff --git a/ledger/payments/invoice/facade.py b/ledger/payments/invoice/facade.py index 66b93e177..2bb0ade4c 100755 --- a/ledger/payments/invoice/facade.py +++ b/ledger/payments/invoice/facade.py @@ -13,13 +13,15 @@ def _get_payment_choice(payment_method): return Invoice.PAYMENT_METHOD_OTHER return None -def create_invoice_crn(order_number, amount, crn_string, system, text, payment_method=None): +def create_invoice_crn(order_number, amount, crn_string, system, text, payment_method=None, invoice_name='', due_date=None): '''Make a new Invoice object using crn ''' inv,created = Invoice.objects.get_or_create( order_number=order_number, amount=amount, - reference = getCRN(crn_string) + reference = getCRN(crn_string), + invoice_name=invoice_name, + due_date=due_date ) print ("create_invoice_crn!!! <<- JASON") @@ -38,7 +40,7 @@ def create_invoice_crn(order_number, amount, crn_string, system, text, payment_m inv.save() return inv -def create_invoice_icrn(order_number, amount, crn_string, _format, system, text, payment_method=None): +def create_invoice_icrn(order_number, amount, crn_string, _format, system, text, payment_method=None, invoice_name='', due_date=None): '''Make a new Invoice object using icrn ''' if _format in ['ICRNDATE','ICRNAMTDATE']: @@ -48,7 +50,9 @@ def create_invoice_icrn(order_number, amount, crn_string, _format, system, text, inv, created = Invoice.objects.get_or_create( order_number=order_number, amount=amount, - reference = icrn + reference = icrn, + invoice_name=invoice_name, + due_date=due_date ) if created: if payment_method: diff --git a/ledger/payments/invoice/migrations/0012_auto_20241107_1451.py b/ledger/payments/invoice/migrations/0012_auto_20241107_1451.py new file mode 100644 index 000000000..d39477ff5 --- /dev/null +++ b/ledger/payments/invoice/migrations/0012_auto_20241107_1451.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-07 06:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('invoice', '0011_auto_20240111_1612'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='due_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='invoice', + name='invoice_name', + field=models.CharField(default='', max_length=255), + ), + ] diff --git a/ledger/payments/invoice/migrations/0013_auto_20241107_1451.py b/ledger/payments/invoice/migrations/0013_auto_20241107_1451.py new file mode 100644 index 000000000..999e3362e --- /dev/null +++ b/ledger/payments/invoice/migrations/0013_auto_20241107_1451.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-07 06:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('invoice', '0012_auto_20241107_1451'), + ] + + operations = [ + migrations.AlterField( + model_name='invoice', + name='invoice_name', + field=models.CharField(blank=True, default='', max_length=255, null=True), + ), + ] diff --git a/ledger/payments/invoice/migrations/0014_auto_20241107_1456.py b/ledger/payments/invoice/migrations/0014_auto_20241107_1456.py new file mode 100644 index 000000000..f998c9c0c --- /dev/null +++ b/ledger/payments/invoice/migrations/0014_auto_20241107_1456.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-07 06:56 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('invoice', '0013_auto_20241107_1451'), + ] + + operations = [ + migrations.AlterField( + model_name='invoice', + name='invoice_name', + field=models.CharField(blank=True, default='', help_text='Is used to override the customer account name.', max_length=255, null=True), + ), + ] diff --git a/ledger/payments/invoice/models.py b/ledger/payments/invoice/models.py index 7878e6924..312512773 100755 --- a/ledger/payments/invoice/models.py +++ b/ledger/payments/invoice/models.py @@ -56,6 +56,7 @@ class Invoice(models.Model): (PAYMENT_METHOD_OTHER, 'Other'), ) + invoice_name = models.CharField(max_length=255, blank=True,null=True, default='', help_text="Is used to override the customer account name.") created = models.DateTimeField(auto_now_add=True) text = models.TextField(null=True,blank=True) amount = models.DecimalField(decimal_places=2,max_digits=12) @@ -66,6 +67,7 @@ class Invoice(models.Model): voided = models.BooleanField(default=False) previous_invoice = models.ForeignKey('self',null=True,blank=True) settlement_date = models.DateField(blank=True, null=True) + due_date = models.DateField(blank=True, null=True) payment_method = models.SmallIntegerField(choices=PAYMENT_METHOD_CHOICES, default=0) oracle_invoice_number = models.CharField(max_length=255, default='', null=True, blank=True) oracle_invoice_file = models.ForeignKey(OracleInvoiceDocument, null=True, blank=True, on_delete=models.SET_NULL, related_name='oracle_invoice_file') diff --git a/ledger/payments/pdf.py b/ledger/payments/pdf.py index 85c02290b..aea450c2e 100755 --- a/ledger/payments/pdf.py +++ b/ledger/payments/pdf.py @@ -162,6 +162,15 @@ def __payment_line(self): self.current_y = current_y def __footer_line(self): + total_cols = 4 + try: + print (self.invoice.due_date.strftime(DATE_FORMAT)) + total_cols = 5 + + except Exception as e: + # if not valid date we keep totals cols at 4 + pass + canvas = self.canv current_y, current_x = self.current_y, self.current_x current_y -= 2 * inch @@ -169,16 +178,27 @@ def __footer_line(self): canvas.setFont(DEFAULT_FONTNAME, LARGE_FONTSIZE) canvas.setFillColor(colors.black) canvas.drawString(current_x, current_y, 'Invoice Number') - canvas.drawString(PAGE_WIDTH/4, current_y, 'Invoice Date') - canvas.drawString((PAGE_WIDTH/4) * 2, current_y, 'GST included') - canvas.drawString((PAGE_WIDTH/4) * 3, current_y, 'Invoice Total') + canvas.drawString(PAGE_WIDTH/total_cols, current_y, 'Invoice Date') + nextrow = 2 + if self.invoice.due_date: + canvas.drawString((PAGE_WIDTH/total_cols) * nextrow, current_y, 'Due Date') + nextrow = nextrow + 1 + canvas.drawString((PAGE_WIDTH/total_cols) * nextrow, current_y, 'GST included') + nextrow = nextrow + 1 + canvas.drawString((PAGE_WIDTH/total_cols) * nextrow, current_y, 'Invoice Total') current_y -= 20 canvas.setFont(DEFAULT_FONTNAME, MEDIUM_FONTSIZE) canvas.drawString(current_x, current_y, self.invoice.reference) - canvas.drawString(PAGE_WIDTH/4, current_y, self.invoice.created.strftime(DATE_FORMAT)) + canvas.drawString(PAGE_WIDTH/total_cols, current_y, self.invoice.created.strftime(DATE_FORMAT)) + nextrow = 2 + if self.invoice.due_date: + canvas.drawString((PAGE_WIDTH/total_cols) * nextrow, current_y, self.invoice.due_date.strftime(DATE_FORMAT)) + nextrow = nextrow + 1 #canvas.drawString((PAGE_WIDTH/4) * 2, current_y, currency(self.invoice.amount - calculate_excl_gst(self.invoice.amount))) - canvas.drawString((PAGE_WIDTH/4) * 2, current_y, currency(total_gst_tax)) - canvas.drawString((PAGE_WIDTH/4) * 3, current_y, currency(self.invoice.amount)) + + canvas.drawString((PAGE_WIDTH/total_cols) * nextrow, current_y, currency(total_gst_tax)) + nextrow = nextrow + 1 + canvas.drawString((PAGE_WIDTH/total_cols) * nextrow, current_y, currency(self.invoice.amount)) def draw(self): if settings.BPAY_ALLOWED: @@ -219,26 +239,42 @@ def _create_header(canvas, doc, draw_page_number=True): canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER), invoice.order.organisation.name) canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) *2, invoice.order.organisation.abn) - canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 3,invoice.owner.get_full_name()) + invoice_name = invoice.owner.get_full_name() + if invoice.invoice_name: + if len(invoice.invoice_name) > 0: + invoice_name = invoice.invoice_name + + canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 3,invoice_name) canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 4,invoice.owner.username) current_x += 452 #write Invoice details + nextrowcount = 2 canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER),'Date') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER),invoice.created.strftime(DATE_FORMAT)) - canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 2, 'Page') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 2, str(canvas.getPageNumber())) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 3, 'Invoice Number') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 3, invoice.reference) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 4, 'Total (AUD)') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 4, currency(invoice.amount)) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 5, 'GST included (AUD)') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER),invoice.created.strftime(DATE_FORMAT)) + canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'Page') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, str(canvas.getPageNumber())) + nextrowcount = nextrowcount + 1 + canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'Invoice Number') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, invoice.reference) + + if invoice.due_date: + nextrowcount = nextrowcount + 1 + canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'Due Date') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, invoice.due_date.strftime(DATE_FORMAT)) + nextrowcount = nextrowcount + 1 + canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'Total (AUD)') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, currency(invoice.amount)) + nextrowcount = nextrowcount + 1 + canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'GST included (AUD)') #canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 5, currency(invoice.amount - calculate_excl_gst(invoice.amount))) - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 5, currency(total_gst_tax)) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 6, 'Paid (AUD)') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 6, currency(invoice.payment_amount)) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 7, 'Outstanding (AUD)') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 7, currency(invoice.balance)) + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, currency(total_gst_tax)) + nextrowcount = nextrowcount + 1 + canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'Paid (AUD)') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, currency(invoice.payment_amount)) + nextrowcount = nextrowcount + 1 + canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, 'Outstanding (AUD)') + canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * nextrowcount, currency(invoice.balance)) canvas.restoreState() def _create_invoice(invoice_buffer, invoice): diff --git a/ledgergw/api.py b/ledgergw/api.py index 2409507e7..4ee100248 100644 --- a/ledgergw/api.py +++ b/ledgergw/api.py @@ -1306,7 +1306,19 @@ def process_create_future_invoice(request,apikey): basket_id = request.POST.get('basket_id','') invoice_text = request.POST.get('invoice_text', '') return_preload_url = request.POST.get('return_preload_url', '') + invoice_name = request.POST.get('invoice_name', '') + due_date_string = request.POST.get('due_date', None) + print ("DUE DATE") + print (due_date_string) + due_date = None + if due_date_string: + try: + due_date = datetime.strptime(due_date_string, "%d/%m/%Y") + print (due_date) + except Exception as e: + print (e) + basket_obj = basket_models.Basket.objects.filter(id=basket_id) if basket_obj.count() > 0: get_basket = basket_models.Basket.objects.get(id=basket_id) @@ -1323,7 +1335,6 @@ def process_create_future_invoice(request,apikey): get_basket.notification_url = return_preload_url get_basket.save() - crn_string = '{0}{1}'.format(systemid_check(get_basket.system),order_number) invoice = invoice_facade.create_invoice_crn( order_number, @@ -1331,8 +1342,11 @@ def process_create_future_invoice(request,apikey): crn_string, get_basket.system, invoice_text, - None + None, + invoice_name, + due_date ) + LinkedInvoiceCreate(invoice, get_basket.id) jsondata['status'] = 200 jsondata['message'] = 'success'