mirror of
https://github.com/mokuappio/serverless-invoices.git
synced 2025-10-27 16:01:07 -04:00
Add invoice validation & prefill invoice data.
This commit is contained in:
@ -1,4 +1,6 @@
|
|||||||
import storage from 'localforage';
|
import storage from 'localforage';
|
||||||
|
import { validate, generateInvoiceNumber } from '@/utils/helpers';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
class InvoiceService {
|
class InvoiceService {
|
||||||
async getInvoices() {
|
async getInvoices() {
|
||||||
@ -12,9 +14,32 @@ class InvoiceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createInvoice(invoice) {
|
async createInvoice(invoice) {
|
||||||
// TODO: add invoice no, issued_at, due_at, late_fee, prefill company info, bank_info, currency, vat_rate
|
const team = storage.getItem('team');
|
||||||
|
|
||||||
const invoices = await this.getInvoices();
|
const invoices = await this.getInvoices();
|
||||||
|
|
||||||
|
invoice.issued_at = dayjs()
|
||||||
|
.format('YYYY-MM-DD');
|
||||||
|
invoice.due_at = dayjs()
|
||||||
|
.add(14, 'days')
|
||||||
|
.format('YYYY-MM-DD');
|
||||||
|
invoice.number = generateInvoiceNumber(dayjs()
|
||||||
|
.format('YYYY'), invoices.length + 1);
|
||||||
|
invoice.late_fee = 0.5;
|
||||||
|
invoice.from_name = team.company_name;
|
||||||
|
invoice.from_address = team.company_address;
|
||||||
|
invoice.from_postal_code = team.from_postal_code;
|
||||||
|
invoice.from_city = team.company_city;
|
||||||
|
invoice.from_country = team.company_country;
|
||||||
|
invoice.from_county = team.company_county;
|
||||||
|
invoice.from_reg_no = team.company_reg_no;
|
||||||
|
invoice.from_vat_no = team.company_vat_no;
|
||||||
|
invoice.from_website = team.website;
|
||||||
|
invoice.from_email = team.contact_email;
|
||||||
|
invoice.from_phone = team.contact_phone;
|
||||||
|
invoice.vat_rate = 0;
|
||||||
|
invoice.currency = 'USD';
|
||||||
|
|
||||||
delete invoice.$id;
|
delete invoice.$id;
|
||||||
delete invoice.$isNew;
|
delete invoice.$isNew;
|
||||||
delete invoice.$isDirty;
|
delete invoice.$isDirty;
|
||||||
@ -24,11 +49,24 @@ class InvoiceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateInvoice(invoice) {
|
async updateInvoice(invoice) {
|
||||||
// TODO: validation
|
const necessaryFields = {
|
||||||
const invoices = await this.getInvoices();
|
currency: 'Currency',
|
||||||
const index = invoices.findIndex(item => item.id === invoice.id);
|
vat_rate: 'Vat Rate',
|
||||||
invoices[index] = invoice;
|
late_fee: 'Late Fee',
|
||||||
return storage.setItem('invoices', invoices);
|
issued_at: 'Issued At',
|
||||||
|
due_at: 'Due At',
|
||||||
|
number: 'Number',
|
||||||
|
};
|
||||||
|
|
||||||
|
const validation = validate(necessaryFields, invoice);
|
||||||
|
|
||||||
|
if (validation.length === 0) {
|
||||||
|
const invoices = await this.getInvoices();
|
||||||
|
const index = invoices.findIndex(item => item.id === invoice.id);
|
||||||
|
invoices[index] = invoice;
|
||||||
|
return storage.setItem('invoices', invoices);
|
||||||
|
}
|
||||||
|
return Promise.reject(validation);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteInvoice(invoiceId) {
|
async deleteInvoice(invoiceId) {
|
||||||
@ -38,10 +76,47 @@ class InvoiceService {
|
|||||||
return storage.setItem('invoices', invoices);
|
return storage.setItem('invoices', invoices);
|
||||||
}
|
}
|
||||||
|
|
||||||
bookInvoice(invoice) {
|
async bookInvoice(invoice) {
|
||||||
// TODO: validation
|
const necessaryFields = {
|
||||||
invoice.status = 'booked';
|
currency: 'Currency',
|
||||||
return this.updateInvoice(invoice);
|
vat_rate: 'Vat rate',
|
||||||
|
late_fee: 'Late fee',
|
||||||
|
issued_at: 'Issued At',
|
||||||
|
due_at: 'Due At',
|
||||||
|
number: 'Number',
|
||||||
|
client_id: 'Client Id',
|
||||||
|
client_name: 'Client Name',
|
||||||
|
client_address: 'Client Address',
|
||||||
|
client_postal_code: 'Client Postal Code',
|
||||||
|
client_city: 'Client City',
|
||||||
|
client_email: 'Client Email',
|
||||||
|
client_country: 'Country',
|
||||||
|
from_name: 'Name',
|
||||||
|
from_address: 'Address',
|
||||||
|
from_postal_code: 'Postal Code',
|
||||||
|
from_country: 'Country/State',
|
||||||
|
from_city: 'City',
|
||||||
|
from_website: 'Website',
|
||||||
|
from_email: 'Your Email',
|
||||||
|
from_phone: 'From Phone',
|
||||||
|
bank_name: 'Bank Name',
|
||||||
|
bank_account_no: 'Bank Account No.',
|
||||||
|
project_id: 'Project Id',
|
||||||
|
rows: {
|
||||||
|
item: 'Item',
|
||||||
|
quantity: 'Quantity',
|
||||||
|
price: 'Price',
|
||||||
|
unit: 'Unit',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const validation = await validate(necessaryFields, invoice);
|
||||||
|
|
||||||
|
if (validation.length === 0) {
|
||||||
|
invoice.status = 'booked';
|
||||||
|
return this.updateInvoice(invoice);
|
||||||
|
}
|
||||||
|
return Promise.reject(validation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -123,7 +123,7 @@ export default {
|
|||||||
commit('clearErrors');
|
commit('clearErrors');
|
||||||
|
|
||||||
return InvoiceService.updateInvoice(getters.invoice)
|
return InvoiceService.updateInvoice(getters.invoice)
|
||||||
.catch(err => commit('setErrors', err.response.data.errors));
|
.catch(err => commit('setErrors', err.errors));
|
||||||
},
|
},
|
||||||
async updateInvoiceRow({ getters, dispatch, commit }, payload) {
|
async updateInvoiceRow({ getters, dispatch, commit }, payload) {
|
||||||
await dispatch('invoiceRowProps', payload);
|
await dispatch('invoiceRowProps', payload);
|
||||||
@ -161,10 +161,12 @@ export default {
|
|||||||
commit('clearErrors');
|
commit('clearErrors');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('tryBookInvoice');
|
||||||
const res = await InvoiceService.bookInvoice(getters.invoice);
|
const res = await InvoiceService.bookInvoice(getters.invoice);
|
||||||
return dispatch('getInvoice', res.invoice_id);
|
return dispatch('getInvoice', res.invoice_id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
commit('setErrors', err.response.data.errors);
|
console.log(err);
|
||||||
|
commit('setErrors', err.errors);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export function uuidv4() {
|
export function uuidv4() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
var r = Math.random() * 16 | 0,
|
||||||
|
v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
return v.toString(16);
|
return v.toString(16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -30,18 +31,42 @@ export function pick(obj, map) {
|
|||||||
return objSubset;
|
return objSubset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function validate(neededFields, fieldsToValidate) {
|
||||||
|
const validationErrors = {};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(neededFields)) {
|
||||||
|
if (Array.isArray(fieldsToValidate[key])) {
|
||||||
|
fieldsToValidate[key].forEach((item) => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!fieldsToValidate.hasOwnProperty(key) || !fieldsToValidate[key] || fieldsToValidate[key].length === 0) {
|
||||||
|
validationErrors[key] = [`Field ${value} is required`];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('errors', validationErrors);
|
||||||
|
return { errors: validationErrors };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function generateInvoiceNumber(date, number) {
|
||||||
|
return `${date}-${number}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function download(data, filename, type) {
|
export function download(data, filename, type) {
|
||||||
var file = new Blob([data], {type: type});
|
var file = new Blob([data], { type: type });
|
||||||
if (window.navigator.msSaveOrOpenBlob) // IE10+
|
if (window.navigator.msSaveOrOpenBlob) // IE10+
|
||||||
|
{
|
||||||
window.navigator.msSaveOrOpenBlob(file, filename);
|
window.navigator.msSaveOrOpenBlob(file, filename);
|
||||||
else { // Others
|
} else { // Others
|
||||||
var a = document.createElement("a"),
|
var a = document.createElement('a'),
|
||||||
url = URL.createObjectURL(file);
|
url = URL.createObjectURL(file);
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = filename;
|
a.download = filename;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user