mirror of
https://github.com/mokuappio/serverless-invoices.git
synced 2025-10-27 16:01:07 -04:00
Do not prompt to update invoice client/team details when closing client/team modal.
Always pass invoice id when updating. When changing team info re-prefill invoice team info.
This commit is contained in:
@ -63,18 +63,18 @@ export default {
|
|||||||
},
|
},
|
||||||
async promptUpdateInvoice() {
|
async promptUpdateInvoice() {
|
||||||
if (this.$route.name === 'invoice' && this.invoice.client_id === this.client.id) {
|
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',
|
okTitle: 'Update',
|
||||||
cancelTitle: 'Dismiss',
|
cancelTitle: 'Dismiss',
|
||||||
cancelVariant: 'btn-link',
|
cancelVariant: 'btn-link',
|
||||||
contentClass: 'bg-base dp--24',
|
contentClass: 'bg-base dp--24',
|
||||||
});
|
});
|
||||||
if (confirmed) {
|
if (confirmed) { */
|
||||||
this.$store.dispatch('invoices/prefillClient', {
|
this.$store.dispatch('invoices/prefillClient', {
|
||||||
client: this.client,
|
client: this.client,
|
||||||
invoice: this.invoice,
|
invoiceId: this.invoice.id,
|
||||||
});
|
});
|
||||||
}
|
/* } */
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export default {
|
|||||||
clientSelected(client) {
|
clientSelected(client) {
|
||||||
this.$store.dispatch('invoices/prefillClient', {
|
this.$store.dispatch('invoices/prefillClient', {
|
||||||
client,
|
client,
|
||||||
invoice: this.invoice,
|
invoiceId: this.invoice.id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export default {
|
|||||||
this.$store.dispatch('invoiceClientFields/updateInvoiceClientField', {
|
this.$store.dispatch('invoiceClientFields/updateInvoiceClientField', {
|
||||||
props,
|
props,
|
||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
|
invoiceId: this.invoice.id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -65,7 +65,10 @@ export default {
|
|||||||
this.$store.dispatch('invoices/bookInvoice');
|
this.$store.dispatch('invoices/bookInvoice');
|
||||||
},
|
},
|
||||||
updateProp(props) {
|
updateProp(props) {
|
||||||
this.$store.dispatch('invoices/updateInvoice', props);
|
this.$store.dispatch('invoices/updateInvoice', {
|
||||||
|
props,
|
||||||
|
invoiceId: this.invoice.id,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
print() {
|
print() {
|
||||||
window.print();
|
window.print();
|
||||||
|
|||||||
@ -101,7 +101,10 @@ export default {
|
|||||||
this.$store.dispatch('invoices/getInvoice', this.$route.params.id);
|
this.$store.dispatch('invoices/getInvoice', this.$route.params.id);
|
||||||
},
|
},
|
||||||
updateProp(props) {
|
updateProp(props) {
|
||||||
this.$store.dispatch('invoices/updateInvoice', props);
|
this.$store.dispatch('invoices/updateInvoice', {
|
||||||
|
props,
|
||||||
|
invoiceId: this.invoice.id,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
addRow() {
|
addRow() {
|
||||||
this.$store.dispatch('invoiceRows/addRow', this.invoice.id);
|
this.$store.dispatch('invoiceRows/addRow', this.invoice.id);
|
||||||
|
|||||||
@ -55,6 +55,7 @@ export default {
|
|||||||
this.$store.dispatch('invoiceRows/updateInvoiceRow', {
|
this.$store.dispatch('invoiceRows/updateInvoiceRow', {
|
||||||
props,
|
props,
|
||||||
id: this.row.id,
|
id: this.row.id,
|
||||||
|
invoiceId: this.row.invoice_id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async removeRow(row) {
|
async removeRow(row) {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export default {
|
|||||||
this.$store.dispatch('invoiceTeamFields/updateInvoiceTeamField', {
|
this.$store.dispatch('invoiceTeamFields/updateInvoiceTeamField', {
|
||||||
props,
|
props,
|
||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
|
invoiceId: this.invoice.id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export default {
|
|||||||
},
|
},
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
team: 'teams/team',
|
team: 'teams/team',
|
||||||
|
invoice: 'invoices/invoice',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -38,9 +39,25 @@ export default {
|
|||||||
getTeam() {
|
getTeam() {
|
||||||
this.$store.dispatch('teams/getTeam');
|
this.$store.dispatch('teams/getTeam');
|
||||||
},
|
},
|
||||||
close() {
|
async close() {
|
||||||
|
await this.promptUpdateInvoice();
|
||||||
this.isOpen = false;
|
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,
|
||||||
|
});
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import storage from 'localforage';
|
import storage from 'localforage';
|
||||||
import TeamService from '@/services/team.service';
|
|
||||||
import {
|
import {
|
||||||
validate, generateInvoiceNumber, removeVuexORMFlags, uuidv4,
|
validate, removeVuexORMFlags,
|
||||||
} from '@/utils/helpers';
|
} from '@/utils/helpers';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
class InvoiceService {
|
class InvoiceService {
|
||||||
async getInvoices() {
|
async getInvoices() {
|
||||||
@ -17,40 +15,7 @@ class InvoiceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createInvoice(invoice) {
|
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;
|
delete invoice.client;
|
||||||
|
|
||||||
|
|
||||||
return this.saveInvoice(invoice);
|
return this.saveInvoice(invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,9 @@ export default {
|
|||||||
},
|
},
|
||||||
async updateInvoiceClientField({ dispatch }, payload) {
|
async updateInvoiceClientField({ dispatch }, payload) {
|
||||||
await dispatch('invoiceClientFieldProps', 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) {
|
async removeInvoiceClientFields(store, invoiceId) {
|
||||||
return InvoiceClientField.delete(field => field.invoice_id === invoiceId);
|
return InvoiceClientField.delete(field => field.invoice_id === invoiceId);
|
||||||
|
|||||||
@ -17,7 +17,9 @@ export default {
|
|||||||
},
|
},
|
||||||
async updateInvoiceRow({ dispatch }, payload) {
|
async updateInvoiceRow({ dispatch }, payload) {
|
||||||
await dispatch('invoiceRowProps', 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) {
|
async addRow(store, invoiceId) {
|
||||||
const row = await InvoiceRow.createNew();
|
const row = await InvoiceRow.createNew();
|
||||||
|
|||||||
@ -15,7 +15,9 @@ export default {
|
|||||||
},
|
},
|
||||||
async updateInvoiceTeamField({ dispatch }, payload) {
|
async updateInvoiceTeamField({ dispatch }, payload) {
|
||||||
await dispatch('invoiceTeamFieldProps', 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) {
|
async addInvoiceTeamField(store, payload) {
|
||||||
const field = await InvoiceTeamField.createNew();
|
const field = await InvoiceTeamField.createNew();
|
||||||
@ -24,8 +26,11 @@ export default {
|
|||||||
invoice_id: payload.invoiceId,
|
invoice_id: payload.invoiceId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async removeInvoiceTeamField(store, fieldId) {
|
async removeInvoiceTeamFields(store, invoiceId) {
|
||||||
await InvoiceTeamField.delete(fieldId);
|
return InvoiceTeamField.delete(field => field.invoice_id === invoiceId);
|
||||||
|
},
|
||||||
|
removeInvoiceTeamField(store, fieldId) {
|
||||||
|
return InvoiceTeamField.delete(fieldId);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
import InvoiceService from '@/services/invoice.service';
|
import InvoiceService from '@/services/invoice.service';
|
||||||
import Invoice from '@/store/models/invoice';
|
import Invoice from '@/store/models/invoice';
|
||||||
import { pick } from '@/utils/helpers';
|
import { generateInvoiceNumber, pick } from '@/utils/helpers';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Errors from '@/utils/errors';
|
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 {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
@ -43,19 +50,22 @@ export default {
|
|||||||
commit('invoiceId', invoiceId);
|
commit('invoiceId', invoiceId);
|
||||||
return invoice;
|
return invoice;
|
||||||
},
|
},
|
||||||
async createNewInvoice() {
|
async createNewInvoice({ dispatch }) {
|
||||||
const invoice = await Invoice.createNew();
|
const invoice = await Invoice.createNew();
|
||||||
await InvoiceService.createInvoice(invoice);
|
await InvoiceService.createInvoice(invoice);
|
||||||
|
await dispatch('prefillTeam', {
|
||||||
|
invoiceId: invoice.id,
|
||||||
|
});
|
||||||
return invoice.id;
|
return invoice.id;
|
||||||
},
|
},
|
||||||
invoiceProps({ state }, props) {
|
invoiceProps(store, payload) {
|
||||||
return Invoice.update({
|
return Invoice.update({
|
||||||
where: state.invoiceId,
|
where: payload.invoiceId,
|
||||||
data: props,
|
data: payload.props,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async updateClient({ getters, dispatch }, props) {
|
async updateClient({ dispatch }, payload) {
|
||||||
const clientProps = pick(props, {
|
const clientProps = pick(payload.props, {
|
||||||
bank_account_id: 'bank_account_id',
|
bank_account_id: 'bank_account_id',
|
||||||
client_name: 'company_name',
|
client_name: 'company_name',
|
||||||
client_address: 'company_address',
|
client_address: 'company_address',
|
||||||
@ -66,19 +76,20 @@ export default {
|
|||||||
client_email: 'invoice_email',
|
client_email: 'invoice_email',
|
||||||
currency: 'currency',
|
currency: 'currency',
|
||||||
});
|
});
|
||||||
if ('vat_rate' in props) {
|
if ('vat_rate' in payload.props) {
|
||||||
clientProps.has_vat = props.vat_rate > 0;
|
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', {
|
dispatch('clients/updateClientById', {
|
||||||
props: clientProps,
|
props: clientProps,
|
||||||
clientId: getters.invoice.client_id,
|
clientId: invoice.client_id,
|
||||||
}, { root: true });
|
}, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateTeam({ getters, dispatch }, props) {
|
async updateTeam({ dispatch }, payload) {
|
||||||
const teamProps = pick(props, {
|
const teamProps = pick(payload.props, {
|
||||||
late_fee: 'invoice_late_fee',
|
late_fee: 'invoice_late_fee',
|
||||||
from_name: 'company_name',
|
from_name: 'company_name',
|
||||||
from_address: 'company_address',
|
from_address: 'company_address',
|
||||||
@ -91,11 +102,13 @@ export default {
|
|||||||
from_phone: 'contact_phone',
|
from_phone: 'contact_phone',
|
||||||
vat_rate: 'vat_rate',
|
vat_rate: 'vat_rate',
|
||||||
});
|
});
|
||||||
if ('due_at' in props || 'issued_at' in props) {
|
const invoice = getInvoice(payload.invoiceId);
|
||||||
teamProps.invoice_due_days = dayjs(getters.invoice.due_at)
|
|
||||||
.diff(getters.invoice.issued_at, 'days');
|
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
|
// 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
|
// This is to avoid setting general VAT to 0, if only changing per invoice
|
||||||
if (parseFloat(teamProps.vat_rate) === 0) {
|
if (parseFloat(teamProps.vat_rate) === 0) {
|
||||||
@ -107,15 +120,15 @@ export default {
|
|||||||
dispatch('teams/updateTeam', teamProps, { root: true });
|
dispatch('teams/updateTeam', teamProps, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateInvoice({ dispatch, commit, getters }, props) {
|
async updateInvoice({ dispatch, commit }, payload) {
|
||||||
if (props) {
|
if (payload.props) {
|
||||||
await dispatch('invoiceProps', props);
|
await dispatch('invoiceProps', payload);
|
||||||
await dispatch('updateClient', props);
|
await dispatch('updateClient', payload);
|
||||||
await dispatch('updateTeam', props);
|
await dispatch('updateTeam', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
commit('clearErrors');
|
commit('clearErrors');
|
||||||
return InvoiceService.updateInvoice(getters.invoice)
|
return InvoiceService.updateInvoice(getInvoice(payload.invoiceId))
|
||||||
.catch(err => commit('setErrors', err.errors));
|
.catch(err => commit('setErrors', err.errors));
|
||||||
},
|
},
|
||||||
async deleteInvoice(store, invoice) {
|
async deleteInvoice(store, invoice) {
|
||||||
@ -135,11 +148,11 @@ export default {
|
|||||||
},
|
},
|
||||||
prefillClient({ dispatch, rootGetters }, payload) {
|
prefillClient({ dispatch, rootGetters }, payload) {
|
||||||
const client = payload.client;
|
const client = payload.client;
|
||||||
dispatch('invoiceClientFields/removeInvoiceClientFields', payload.invoice.id, { root: true });
|
dispatch('invoiceClientFields/removeInvoiceClientFields', payload.invoiceId, { root: true });
|
||||||
|
|
||||||
client.fields.forEach((field) => {
|
client.fields.forEach((field) => {
|
||||||
dispatch('invoiceClientFields/addInvoiceClientField', {
|
dispatch('invoiceClientFields/addInvoiceClientField', {
|
||||||
invoiceId: payload.invoice.id,
|
invoiceId: payload.invoiceId,
|
||||||
props: {
|
props: {
|
||||||
label: field.label,
|
label: field.label,
|
||||||
value: field.value,
|
value: field.value,
|
||||||
@ -149,27 +162,65 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return dispatch('updateInvoice', {
|
return dispatch('updateInvoice', {
|
||||||
client_id: client.id,
|
invoiceId: payload.invoiceId,
|
||||||
client_name: client.company_name,
|
props: {
|
||||||
client_address: client.company_address,
|
client_id: client.id,
|
||||||
client_postal_code: client.company_postal_code,
|
client_name: client.company_name,
|
||||||
client_city: client.company_city,
|
client_address: client.company_address,
|
||||||
client_county: client.company_county,
|
client_postal_code: client.company_postal_code,
|
||||||
client_country: client.company_country,
|
client_city: client.company_city,
|
||||||
client_email: client.invoice_email,
|
client_county: client.company_county,
|
||||||
currency: client.currency || rootGetters['teams/team'].currency || 'USD',
|
client_country: client.company_country,
|
||||||
vat_rate: client.has_vat ? rootGetters['teams/team'].vat_rate : 0,
|
client_email: client.invoice_email,
|
||||||
bank_name: client.bank_account ? client.bank_account.bank_name : null,
|
currency: client.currency || rootGetters['teams/team'].currency || 'USD',
|
||||||
bank_account_no: client.bank_account ? client.bank_account.account_no : null,
|
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: {
|
getters: {
|
||||||
invoice(state) {
|
invoice(state) {
|
||||||
return Invoice.query()
|
return getInvoice(state.invoiceId);
|
||||||
.with(['client', 'client_fields', 'team_fields'])
|
|
||||||
.with('rows', query => query.orderBy('order', 'asc'))
|
|
||||||
.find(state.invoiceId);
|
|
||||||
},
|
},
|
||||||
all() {
|
all() {
|
||||||
return Invoice.query()
|
return Invoice.query()
|
||||||
|
|||||||
Reference in New Issue
Block a user