mirror of
				https://github.com/mokuappio/serverless-invoices.git
				synced 2025-10-27 16:01:07 -04:00 
			
		
		
		
	Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
		| @ -1,4 +1,6 @@ | ||||
| import storage from 'localforage'; | ||||
| import { validate, generateInvoiceNumber } from '@/utils/helpers'; | ||||
| import dayjs from 'dayjs'; | ||||
|  | ||||
| class InvoiceService { | ||||
|   async getInvoices() { | ||||
| @ -12,9 +14,32 @@ class InvoiceService { | ||||
|   } | ||||
|  | ||||
|   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(); | ||||
|  | ||||
|     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.$isNew; | ||||
|     delete invoice.$isDirty; | ||||
| @ -24,12 +49,25 @@ class InvoiceService { | ||||
|   } | ||||
|  | ||||
|   async updateInvoice(invoice) { | ||||
|     // TODO: validation | ||||
|     const necessaryFields = { | ||||
|       currency: 'Currency', | ||||
|       vat_rate: 'Vat Rate', | ||||
|       late_fee: 'Late Fee', | ||||
|       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) { | ||||
|     const invoices = await this.getInvoices(); | ||||
| @ -38,11 +76,48 @@ class InvoiceService { | ||||
|     return storage.setItem('invoices', invoices); | ||||
|   } | ||||
|  | ||||
|   bookInvoice(invoice) { | ||||
|     // TODO: validation | ||||
|   async bookInvoice(invoice) { | ||||
|     const necessaryFields = { | ||||
|       currency: 'Currency', | ||||
|       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); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default new InvoiceService(); | ||||
|  | ||||
| @ -123,7 +123,7 @@ export default { | ||||
|       commit('clearErrors'); | ||||
|  | ||||
|       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) { | ||||
|       await dispatch('invoiceRowProps', payload); | ||||
| @ -161,10 +161,12 @@ export default { | ||||
|       commit('clearErrors'); | ||||
|  | ||||
|       try { | ||||
|         console.log('tryBookInvoice'); | ||||
|         const res = await InvoiceService.bookInvoice(getters.invoice); | ||||
|         return dispatch('getInvoice', res.invoice_id); | ||||
|       } catch (err) { | ||||
|         commit('setErrors', err.response.data.errors); | ||||
|         console.log(err); | ||||
|         commit('setErrors', err.errors); | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| /* eslint-disable */ | ||||
| export function uuidv4() { | ||||
|   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); | ||||
|   }); | ||||
| } | ||||
| @ -30,18 +31,42 @@ export function pick(obj, map) { | ||||
|   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) { | ||||
|   var file = new Blob([data], {type: type}); | ||||
|   var file = new Blob([data], { type: type }); | ||||
|   if (window.navigator.msSaveOrOpenBlob) // IE10+ | ||||
|   { | ||||
|     window.navigator.msSaveOrOpenBlob(file, filename); | ||||
|   else { // Others | ||||
|     var a = document.createElement("a"), | ||||
|   } else { // Others | ||||
|     var a = document.createElement('a'), | ||||
|       url = URL.createObjectURL(file); | ||||
|     a.href = url; | ||||
|     a.download = filename; | ||||
|     document.body.appendChild(a); | ||||
|     a.click(); | ||||
|     setTimeout(function() { | ||||
|     setTimeout(function () { | ||||
|       document.body.removeChild(a); | ||||
|       window.URL.revokeObjectURL(url); | ||||
|     }, 0); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user