diff --git a/src/components/clients/ClientModal.vue b/src/components/clients/ClientModal.vue index 86d7e9e..60676e4 100644 --- a/src/components/clients/ClientModal.vue +++ b/src/components/clients/ClientModal.vue @@ -63,18 +63,18 @@ export default { }, async promptUpdateInvoice() { if (this.$route.name === 'invoice' && this.invoice.client_id === this.client.id) { - const confirmed = await this.$bvModal.msgBoxConfirm('Update client details on invoice?', { + /* const confirmed = await this.$bvModal.msgBoxConfirm('Update client details on invoice?', { okTitle: 'Update', cancelTitle: 'Dismiss', cancelVariant: 'btn-link', contentClass: 'bg-base dp--24', }); - if (confirmed) { - this.$store.dispatch('invoices/prefillClient', { - client: this.client, - invoice: this.invoice, - }); - } + if (confirmed) { */ + this.$store.dispatch('invoices/prefillClient', { + client: this.client, + invoiceId: this.invoice.id, + }); + /* } */ } }, }, diff --git a/src/components/invoices/InvoiceClientDetails.vue b/src/components/invoices/InvoiceClientDetails.vue index d55db1f..749d171 100644 --- a/src/components/invoices/InvoiceClientDetails.vue +++ b/src/components/invoices/InvoiceClientDetails.vue @@ -65,7 +65,7 @@ export default { clientSelected(client) { this.$store.dispatch('invoices/prefillClient', { client, - invoice: this.invoice, + invoiceId: this.invoice.id, }); }, }, diff --git a/src/components/invoices/InvoiceClientFields.vue b/src/components/invoices/InvoiceClientFields.vue index 8d517b8..5537c99 100644 --- a/src/components/invoices/InvoiceClientFields.vue +++ b/src/components/invoices/InvoiceClientFields.vue @@ -22,6 +22,7 @@ export default { this.$store.dispatch('invoiceClientFields/updateInvoiceClientField', { props, fieldId: field.id, + invoiceId: this.invoice.id, }); }, }, diff --git a/src/components/invoices/InvoiceControls.vue b/src/components/invoices/InvoiceControls.vue index e642e0f..6fd7546 100644 --- a/src/components/invoices/InvoiceControls.vue +++ b/src/components/invoices/InvoiceControls.vue @@ -65,7 +65,10 @@ export default { this.$store.dispatch('invoices/bookInvoice'); }, updateProp(props) { - this.$store.dispatch('invoices/updateInvoice', props); + this.$store.dispatch('invoices/updateInvoice', { + props, + invoiceId: this.invoice.id, + }); }, print() { window.print(); diff --git a/src/components/invoices/InvoiceForm.vue b/src/components/invoices/InvoiceForm.vue index 3d073a8..0190ebc 100644 --- a/src/components/invoices/InvoiceForm.vue +++ b/src/components/invoices/InvoiceForm.vue @@ -101,7 +101,10 @@ export default { this.$store.dispatch('invoices/getInvoice', this.$route.params.id); }, updateProp(props) { - this.$store.dispatch('invoices/updateInvoice', props); + this.$store.dispatch('invoices/updateInvoice', { + props, + invoiceId: this.invoice.id, + }); }, addRow() { this.$store.dispatch('invoiceRows/addRow', this.invoice.id); diff --git a/src/components/invoices/InvoiceRow.vue b/src/components/invoices/InvoiceRow.vue index 4a8a5be..efab2d9 100644 --- a/src/components/invoices/InvoiceRow.vue +++ b/src/components/invoices/InvoiceRow.vue @@ -55,6 +55,7 @@ export default { this.$store.dispatch('invoiceRows/updateInvoiceRow', { props, id: this.row.id, + invoiceId: this.row.invoice_id, }); }, async removeRow(row) { diff --git a/src/components/invoices/InvoiceTeamFields.vue b/src/components/invoices/InvoiceTeamFields.vue index 2526099..4d21645 100644 --- a/src/components/invoices/InvoiceTeamFields.vue +++ b/src/components/invoices/InvoiceTeamFields.vue @@ -22,6 +22,7 @@ export default { this.$store.dispatch('invoiceTeamFields/updateInvoiceTeamField', { props, fieldId: field.id, + invoiceId: this.invoice.id, }); }, }, diff --git a/src/components/team/TeamModal.vue b/src/components/team/TeamModal.vue index 4e7edfc..d9f6ff2 100644 --- a/src/components/team/TeamModal.vue +++ b/src/components/team/TeamModal.vue @@ -29,6 +29,7 @@ export default { }, ...mapGetters({ team: 'teams/team', + invoice: 'invoices/invoice', }), }, mounted() { @@ -38,9 +39,25 @@ export default { getTeam() { this.$store.dispatch('teams/getTeam'); }, - close() { + async close() { + await this.promptUpdateInvoice(); this.isOpen = false; }, + async promptUpdateInvoice() { + if (this.$route.name === 'invoice') { + /* const confirmed = await this.$bvModal.msgBoxConfirm('Update team details on invoice?', { + okTitle: 'Update', + cancelTitle: 'Dismiss', + cancelVariant: 'btn-link', + contentClass: 'bg-base dp--24', + }); + if (confirmed) { */ + this.$store.dispatch('invoices/prefillTeam', { + invoiceId: this.invoice.id, + }); + /* } */ + } + }, }, }; diff --git a/src/services/invoice.service.js b/src/services/invoice.service.js index 717b002..114b125 100644 --- a/src/services/invoice.service.js +++ b/src/services/invoice.service.js @@ -1,9 +1,7 @@ import storage from 'localforage'; -import TeamService from '@/services/team.service'; import { - validate, generateInvoiceNumber, removeVuexORMFlags, uuidv4, + validate, removeVuexORMFlags, } from '@/utils/helpers'; -import dayjs from 'dayjs'; class InvoiceService { async getInvoices() { @@ -17,40 +15,7 @@ class InvoiceService { } async createInvoice(invoice) { - const team = await TeamService.getTeam(); - - const invoices = await this.getInvoices(); - - invoice.issued_at = dayjs() - .format('YYYY-MM-DD'); - invoice.due_at = dayjs() - .add(team.invoice_due_days || 14, 'days') - .format('YYYY-MM-DD'); - invoice.number = generateInvoiceNumber(invoices); - invoice.late_fee = team.invoice_late_fee || 0.5; - invoice.from_name = team.company_name; - invoice.from_address = team.company_address; - invoice.from_postal_code = team.company_postal_code; - invoice.from_city = team.company_city; - invoice.from_country = team.company_country; - invoice.from_county = team.company_county; - invoice.from_website = team.website; - invoice.from_email = team.contact_email; - invoice.from_phone = team.contact_phone; - invoice.vat_rate = team.vat_rate || 0; - invoice.currency = team.currency || 'USD'; - - // Add custom fields - invoice.team_fields = team.fields.map(field => ({ - id: uuidv4(), - label: field.label, - value: field.value, - invoice_id: invoice.id, - })); - delete invoice.client; - - return this.saveInvoice(invoice); } diff --git a/src/store/invoice-client-fields.js b/src/store/invoice-client-fields.js index 35826ff..3bdea20 100644 --- a/src/store/invoice-client-fields.js +++ b/src/store/invoice-client-fields.js @@ -15,7 +15,9 @@ export default { }, async updateInvoiceClientField({ dispatch }, payload) { await dispatch('invoiceClientFieldProps', payload); - return dispatch('invoices/updateInvoice', null, { root: true }); + return dispatch('invoices/updateInvoice', { + invoiceId: payload.invoiceId, + }, { root: true }); }, async removeInvoiceClientFields(store, invoiceId) { return InvoiceClientField.delete(field => field.invoice_id === invoiceId); diff --git a/src/store/invoice-rows.js b/src/store/invoice-rows.js index 196eb25..b6f64b5 100644 --- a/src/store/invoice-rows.js +++ b/src/store/invoice-rows.js @@ -17,7 +17,9 @@ export default { }, async updateInvoiceRow({ dispatch }, payload) { await dispatch('invoiceRowProps', payload); - return dispatch('invoices/updateInvoice', null, { root: true }); + return dispatch('invoices/updateInvoice', { + invoiceId: payload.invoiceId, + }, { root: true }); }, async addRow(store, invoiceId) { const row = await InvoiceRow.createNew(); diff --git a/src/store/invoice-team-fields.js b/src/store/invoice-team-fields.js index 88ad7ff..e0e14b9 100644 --- a/src/store/invoice-team-fields.js +++ b/src/store/invoice-team-fields.js @@ -15,7 +15,9 @@ export default { }, async updateInvoiceTeamField({ dispatch }, payload) { await dispatch('invoiceTeamFieldProps', payload); - return dispatch('invoices/updateInvoice', null, { root: true }); + return dispatch('invoices/updateInvoice', { + invoiceId: payload.invoiceId, + }, { root: true }); }, async addInvoiceTeamField(store, payload) { const field = await InvoiceTeamField.createNew(); @@ -24,8 +26,11 @@ export default { invoice_id: payload.invoiceId, }); }, - async removeInvoiceTeamField(store, fieldId) { - await InvoiceTeamField.delete(fieldId); + async removeInvoiceTeamFields(store, invoiceId) { + return InvoiceTeamField.delete(field => field.invoice_id === invoiceId); + }, + removeInvoiceTeamField(store, fieldId) { + return InvoiceTeamField.delete(fieldId); }, }, }; diff --git a/src/store/invoices.js b/src/store/invoices.js index 4899071..89f90ad 100644 --- a/src/store/invoices.js +++ b/src/store/invoices.js @@ -1,9 +1,16 @@ import InvoiceService from '@/services/invoice.service'; import Invoice from '@/store/models/invoice'; -import { pick } from '@/utils/helpers'; +import { generateInvoiceNumber, pick } from '@/utils/helpers'; import dayjs from 'dayjs'; import Errors from '@/utils/errors'; +function getInvoice(invoiceId) { + return Invoice.query() + .with(['client', 'client_fields', 'team_fields']) + .with('rows', query => query.orderBy('order', 'asc')) + .find(invoiceId); +} + export default { namespaced: true, state: { @@ -43,19 +50,22 @@ export default { commit('invoiceId', invoiceId); return invoice; }, - async createNewInvoice() { + async createNewInvoice({ dispatch }) { const invoice = await Invoice.createNew(); await InvoiceService.createInvoice(invoice); + await dispatch('prefillTeam', { + invoiceId: invoice.id, + }); return invoice.id; }, - invoiceProps({ state }, props) { + invoiceProps(store, payload) { return Invoice.update({ - where: state.invoiceId, - data: props, + where: payload.invoiceId, + data: payload.props, }); }, - async updateClient({ getters, dispatch }, props) { - const clientProps = pick(props, { + async updateClient({ dispatch }, payload) { + const clientProps = pick(payload.props, { bank_account_id: 'bank_account_id', client_name: 'company_name', client_address: 'company_address', @@ -66,19 +76,20 @@ export default { client_email: 'invoice_email', currency: 'currency', }); - if ('vat_rate' in props) { - clientProps.has_vat = props.vat_rate > 0; + if ('vat_rate' in payload.props) { + clientProps.has_vat = payload.props.vat_rate > 0; } + const invoice = getInvoice(payload.invoiceId); - if (Object.keys(clientProps).length > 0 && getters.invoice.client_id) { + if (Object.keys(clientProps).length > 0 && invoice.client_id) { dispatch('clients/updateClientById', { props: clientProps, - clientId: getters.invoice.client_id, + clientId: invoice.client_id, }, { root: true }); } }, - async updateTeam({ getters, dispatch }, props) { - const teamProps = pick(props, { + async updateTeam({ dispatch }, payload) { + const teamProps = pick(payload.props, { late_fee: 'invoice_late_fee', from_name: 'company_name', from_address: 'company_address', @@ -91,11 +102,13 @@ export default { from_phone: 'contact_phone', vat_rate: 'vat_rate', }); - if ('due_at' in props || 'issued_at' in props) { - teamProps.invoice_due_days = dayjs(getters.invoice.due_at) - .diff(getters.invoice.issued_at, 'days'); + const invoice = getInvoice(payload.invoiceId); + + if ('due_at' in payload.props || 'issued_at' in payload.props) { + teamProps.invoice_due_days = dayjs(invoice.due_at) + .diff(invoice.issued_at, 'days'); } - if ('vat_rate' in props) { + if ('vat_rate' in payload.props) { // You can only set VAT to 0 if setting it directly under settings // This is to avoid setting general VAT to 0, if only changing per invoice if (parseFloat(teamProps.vat_rate) === 0) { @@ -107,15 +120,15 @@ export default { dispatch('teams/updateTeam', teamProps, { root: true }); } }, - async updateInvoice({ dispatch, commit, getters }, props) { - if (props) { - await dispatch('invoiceProps', props); - await dispatch('updateClient', props); - await dispatch('updateTeam', props); + async updateInvoice({ dispatch, commit }, payload) { + if (payload.props) { + await dispatch('invoiceProps', payload); + await dispatch('updateClient', payload); + await dispatch('updateTeam', payload); } commit('clearErrors'); - return InvoiceService.updateInvoice(getters.invoice) + return InvoiceService.updateInvoice(getInvoice(payload.invoiceId)) .catch(err => commit('setErrors', err.errors)); }, async deleteInvoice(store, invoice) { @@ -135,11 +148,11 @@ export default { }, prefillClient({ dispatch, rootGetters }, payload) { const client = payload.client; - dispatch('invoiceClientFields/removeInvoiceClientFields', payload.invoice.id, { root: true }); + dispatch('invoiceClientFields/removeInvoiceClientFields', payload.invoiceId, { root: true }); client.fields.forEach((field) => { dispatch('invoiceClientFields/addInvoiceClientField', { - invoiceId: payload.invoice.id, + invoiceId: payload.invoiceId, props: { label: field.label, value: field.value, @@ -149,27 +162,65 @@ export default { }); return dispatch('updateInvoice', { - client_id: client.id, - client_name: client.company_name, - client_address: client.company_address, - client_postal_code: client.company_postal_code, - client_city: client.company_city, - client_county: client.company_county, - client_country: client.company_country, - client_email: client.invoice_email, - currency: client.currency || rootGetters['teams/team'].currency || 'USD', - vat_rate: client.has_vat ? rootGetters['teams/team'].vat_rate : 0, - bank_name: client.bank_account ? client.bank_account.bank_name : null, - bank_account_no: client.bank_account ? client.bank_account.account_no : null, + invoiceId: payload.invoiceId, + props: { + client_id: client.id, + client_name: client.company_name, + client_address: client.company_address, + client_postal_code: client.company_postal_code, + client_city: client.company_city, + client_county: client.company_county, + client_country: client.company_country, + client_email: client.invoice_email, + currency: client.currency || rootGetters['teams/team'].currency || 'USD', + vat_rate: client.has_vat ? rootGetters['teams/team'].vat_rate : 0, + bank_name: client.bank_account ? client.bank_account.bank_name : null, + bank_account_no: client.bank_account ? client.bank_account.account_no : null, + }, + }); + }, + prefillTeam({ dispatch, getters, rootGetters }, payload) { + const team = rootGetters['teams/team']; + dispatch('invoiceTeamFields/removeInvoiceTeamFields', payload.invoiceId, { root: true }); + + team.fields.forEach((field) => { + dispatch('invoiceTeamFields/addInvoiceTeamField', { + invoiceId: payload.invoiceId, + props: { + label: field.label, + value: field.value, + }, + }, { root: true }); + }); + + return dispatch('updateInvoice', { + invoiceId: payload.invoiceId, + props: { + issued_at: dayjs() + .format('YYYY-MM-DD'), + due_at: dayjs() + .add(team.invoice_due_days || 14, 'days') + .format('YYYY-MM-DD'), + number: generateInvoiceNumber(getters.all), + late_fee: team.invoice_late_fee || 0.5, + from_name: team.company_name, + from_address: team.company_address, + from_postal_code: team.company_postal_code, + from_city: team.company_city, + from_country: team.company_country, + from_county: team.company_county, + from_website: team.website, + from_email: team.contact_email, + from_phone: team.contact_phone, + vat_rate: team.vat_rate || 0, + currency: team.currency || 'USD', + }, }); }, }, getters: { invoice(state) { - return Invoice.query() - .with(['client', 'client_fields', 'team_fields']) - .with('rows', query => query.orderBy('order', 'asc')) - .find(state.invoiceId); + return getInvoice(state.invoiceId); }, all() { return Invoice.query()