jcloude/dashboard/src/pages/BillingInvoices.vue

251 lines
6.2 KiB
Vue

<template>
<div class="p-5">
<ObjectList :options="options" />
<Dialog
v-model="invoiceDialog"
:options="{ size: '3xl', title: showInvoice?.name }"
>
<template #body-content>
<template v-if="showInvoice">
<div
v-if="showInvoice.status === 'Empty'"
class="text-base text-gray-600"
>
Nothing to show
</div>
<InvoiceTable v-else :invoiceId="showInvoice.name" />
</template>
</template>
</Dialog>
<AddPrepaidCreditsDialog
v-if="showBuyPrepaidCreditsDialog"
v-model="showBuyPrepaidCreditsDialog"
:minimumAmount="minimumAmount"
:docName="invoiceForPayment"
:type="'Invoice'"
@success="
() => {
showBuyPrepaidCreditsDialog = false;
invoiceForPayment = null;
}
"
/>
</div>
</template>
<script>
import { h } from 'vue';
import { Button } from 'jingrow-ui';
import ObjectList from '../components/ObjectList.vue';
import InvoiceTable from '../components/InvoiceTable.vue';
import { userCurrency, date } from '../utils/format';
import { confirmDialog, icon, renderInDialog } from '../utils/components';
import AddPrepaidCreditsDialog from '../components/billing/AddPrepaidCreditsDialog.vue';
import { dayjsLocal } from '../utils/dayjs';
import router from '../router';
export default {
name: 'BillingInvoices',
props: ['tab'],
components: {
ObjectList,
InvoiceTable,
AddPrepaidCreditsDialog,
},
data() {
return {
invoiceDialog: false,
showInvoice: null,
showBuyPrepaidCreditsDialog: false,
minimumAmount: 0,
};
},
computed: {
options() {
return {
pagetype: 'Invoice',
fields: [
'type',
'invoice_pdf',
'payment_mode',
'stripe_invoice_url',
'due_date',
'period_start',
'period_end',
'mpesa_invoice',
'mpesa_invoice_pdf',
],
filterControls: () => {
return [
{
type: 'select',
label: 'Type',
class: !this.$isMobile ? 'w-36' : '',
fieldname: 'type',
options: ['', 'Subscription', 'Prepaid Credits'],
},
{
type: 'select',
label: 'Status',
class: !this.$isMobile ? 'w-36' : '',
fieldname: 'status',
options: [
'',
'Draft',
'Invoice Created',
'Unpaid',
'Paid',
'Refunded',
'Uncollectible',
'Collected',
'Empty',
],
},
];
},
columns: [
{
label: 'Invoice',
fieldname: 'name',
class: 'font-medium',
format(value, row) {
if (row.type == 'Subscription') {
let end = dayjsLocal(row.period_end);
return end.format('MMMM YYYY');
} else if (row.type == 'Partnership Fees') {
return 'Partnership Fees';
}
return 'Prepaid Credits';
},
width: 0.8,
},
{
label: 'Status',
fieldname: 'status',
type: 'Badge',
width: '150px',
},
{
label: 'Date',
fieldname: 'due_date',
format(value, row) {
if (row.type == 'Subscription') {
let start = dayjsLocal(row.period_start);
let end = dayjsLocal(row.period_end);
let sameYear = start.year() === end.year();
let formattedStart = sameYear
? start.format('MMM D')
: start.format('ll');
return `${formattedStart} - ${end.format('ll')}`;
}
return date(value, 'll');
},
},
{
label: 'Total',
fieldname: 'total',
format: this.formatCurrency,
align: 'right',
width: 0.6,
},
{
label: 'Amount Paid',
fieldname: 'amount_paid',
format: this.formatCurrency,
align: 'right',
width: 0.6,
},
{
label: 'Amount Due',
fieldname: 'amount_due',
format: this.formatCurrency,
align: 'right',
width: 0.6,
},
{
label: '',
type: 'Button',
align: 'right',
Button: ({ row }) => {
if (row.invoice_pdf || row.mpesa_invoice_pdf) {
return {
label: 'Download Invoice',
slots: {
prefix: icon('download'),
},
onClick: () => {
if (row.mpesa_invoice_pdf) {
window.open(row.mpesa_invoice_pdf);
} else {
window.open(row.invoice_pdf);
}
},
};
}
if (row.status === 'Unpaid' && row.amount_due > 0) {
return {
label: 'Pay Now',
slots: {
prefix: icon('external-link'),
},
onClick: (e) => {
e.stopPropagation();
if (row.stripe_invoice_url && row.payment_mode == 'Card') {
window.open(
`/api/action/jcloude.api.client.run_pg_method?dt=Invoice&dn=${row.name}&method=stripe_payment_url`,
);
} else {
this.showBuyPrepaidCreditsDialog = true;
this.minimumAmount = row.amount_due;
this.invoiceForPayment = row.name;
}
},
};
}
},
prefix(row) {
if (row.stripe_payment_failed && row.status !== 'Paid') {
return h(Button, {
variant: 'ghost',
theme: 'red',
icon: 'alert-circle',
onClick(e) {
e.stopPropagation();
confirmDialog({
title: 'Payment Failed',
message: `<div class="space-y-4"><p class="text-base">Your payment with the card ending <strong>${row.stripe_payment_failed_card}</strong> failed for this invoice due to the following reason:</p><div class="text-sm font-mono text-gray-600 rounded p-2 bg-gray-100">${row.stripe_payment_error}</div><p class="text-base">Please change your payment method to pay this invoice.</p></div>`,
primaryAction: {
label: 'Change Payment Method',
variant: 'solid',
onClick: ({ hide }) => {
hide();
router.push({
name: 'BillingPaymentMethods',
});
},
},
});
},
});
}
},
},
],
orderBy: 'due_date desc, creation desc',
onRowClick: (row) => {
this.showInvoice = row;
this.invoiceDialog = true;
},
};
},
},
methods: {
formatCurrency(value) {
if (value === 0) {
return '';
}
return userCurrency(value);
},
},
};
</script>