mirror of
https://github.com/mokuappio/serverless-invoices.git
synced 2025-10-27 16:01:07 -04:00
Added multiple taxes for invoice rows. Client has_vat => has_tax.
Abstracted add row button to separate component. Abstracted invoice row headers to separate component. Remove vat related things, now replaced with custom taxes. Invoice tax totals are calculated per tax based on invoice rows.
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@ -7378,10 +7378,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
"dayjs": "^1.10.3",
|
||||
"es6-promise": "^4.2.6",
|
||||
"localforage": "^1.9.0",
|
||||
"lodash": "^4.17.21",
|
||||
"vue": "^2.6.10",
|
||||
"vue-autosuggest": "^2.2.0",
|
||||
"vue-multiselect": "^2.1.6",
|
||||
|
||||
@ -41,8 +41,8 @@
|
||||
label="Currency" field="currency" :errors="errors" class="col-sm-4"/>
|
||||
<AppInput :value="client.rate" @change="updateProp({ rate: $event })"
|
||||
label="Hourly rate" field="rate" :errors="errors" class="col-sm-4"/>
|
||||
<AppCheckbox :value="client.has_vat" @input="updateProp({ has_vat: $event })"
|
||||
label="Apply VAT" field="has_vat" :errors="errors" class="col-sm-4"/>
|
||||
<AppCheckbox :value="client.has_tax" @input="updateProp({ has_tax: $event })"
|
||||
label="Apply taxes" field="has_tax" :errors="errors" class="col-sm-4"/>
|
||||
<AppSelect :value="client.bank_account"
|
||||
track-by="id"
|
||||
label="Bank account"
|
||||
|
||||
34
src/components/invoices/InvoiceAddRowBtn.vue
Normal file
34
src/components/invoices/InvoiceAddRowBtn.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<tr class="d-print-none">
|
||||
<td :colspan="colspan">
|
||||
<button class="btn btn-sm" @click="addRow">
|
||||
<i class="material-icons md-18 pointer">add</i>
|
||||
</button>
|
||||
<AppError :errors="errors" field="rows"/>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import AppError from '@/components/form/AppError';
|
||||
|
||||
export default {
|
||||
props: ['invoice', 'errors'],
|
||||
components: {
|
||||
AppError,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
taxes: 'invoiceRows/taxes',
|
||||
}),
|
||||
colspan() {
|
||||
return 5 + this.taxes.length;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addRow() {
|
||||
this.$store.dispatch('invoiceRows/addRow', this.invoice.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -20,26 +20,11 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Unit</th>
|
||||
<th>Price</th>
|
||||
<th class="text-right">Sum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<InvoiceRowsHeader :invoice="invoice"/>
|
||||
<tbody>
|
||||
<InvoiceRow v-for="(row, index) in invoice.rows" :errors="errors"
|
||||
:row="row" :index="index" :key="row.id"/>
|
||||
<tr class="d-print-none">
|
||||
<td colspan="5">
|
||||
<button class="btn btn-sm" @click="addRow">
|
||||
<i class="material-icons md-18 pointer">add</i>
|
||||
</button>
|
||||
<AppError :errors="errors" field="rows"/>
|
||||
</td>
|
||||
</tr>
|
||||
<InvoiceAddRowBtn :invoice="invoice" :errors="errors"/>
|
||||
</tbody>
|
||||
<InvoiceTotals :invoice="invoice" :errors="errors" @update="updateProp"/>
|
||||
</table>
|
||||
@ -64,11 +49,13 @@ import InvoiceContactDetails from '@/components/invoices/InvoiceContactDetails';
|
||||
import InvoiceHeader from '@/components/invoices/InvoiceHeader';
|
||||
import InvoiceTotals from '@/components/invoices/InvoiceTotals';
|
||||
import AppEditable from '@/components/form/AppEditable';
|
||||
import AppError from '@/components/form/AppError';
|
||||
import TeamLogo from '@/components/team/TeamLogo';
|
||||
import InvoiceRowsHeader from '@/components/invoices/InvoiceRowsHeader';
|
||||
import InvoiceAddRowBtn from '@/components/invoices/InvoiceAddRowBtn';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InvoiceAddRowBtn,
|
||||
TeamLogo,
|
||||
InvoiceTotals,
|
||||
InvoiceHeader,
|
||||
@ -76,9 +63,9 @@ export default {
|
||||
InvoiceBankDetails,
|
||||
InvoiceCompanyDetails,
|
||||
InvoiceRow,
|
||||
InvoiceRowsHeader,
|
||||
InvoiceClientDetails,
|
||||
AppEditable,
|
||||
AppError,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@ -106,9 +93,6 @@ export default {
|
||||
invoiceId: this.invoice.id,
|
||||
});
|
||||
},
|
||||
addRow() {
|
||||
this.$store.dispatch('invoiceRows/addRow', this.invoice.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -28,6 +28,14 @@
|
||||
placeholder="Enter price"
|
||||
@change="updateProp({ price: $event })"/>
|
||||
</td>
|
||||
<td v-for="(tax, taxIndex) in row.taxes" :title="tax.label">
|
||||
<AppEditable v-if="tax.row_id"
|
||||
:value="tax.value | currency"
|
||||
:errors="errors"
|
||||
:field="`rows.${index}.taxes.${taxIndex}.value`"
|
||||
placeholder="Enter tax"
|
||||
@change="updateTaxProp({ value: $event }, tax)"/>
|
||||
</td>
|
||||
<td class="text-right position-relative">
|
||||
{{ (row.quantity * row.price) | currency }}
|
||||
<button class="btn btn-sm remove-invoice-row d-print-none" @click="removeRow(row)">
|
||||
@ -58,6 +66,13 @@ export default {
|
||||
invoiceId: this.row.invoice_id,
|
||||
});
|
||||
},
|
||||
updateTaxProp(props, tax) {
|
||||
this.$store.dispatch('invoiceRows/updateInvoiceRowTax', {
|
||||
props,
|
||||
invoiceId: this.row.invoice_id,
|
||||
taxId: tax.id,
|
||||
});
|
||||
},
|
||||
async removeRow(row) {
|
||||
await this.$store.dispatch('invoiceRows/removeRow', row.id);
|
||||
this.updateProp();
|
||||
|
||||
27
src/components/invoices/InvoiceRowsHeader.vue
Normal file
27
src/components/invoices/InvoiceRowsHeader.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Unit</th>
|
||||
<th>Price</th>
|
||||
<th v-for="tax in taxes" :key="tax.id">
|
||||
{{ tax.label }} %
|
||||
</th>
|
||||
<th class="text-right">Sum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
taxes: 'invoiceRows/taxes',
|
||||
}),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -1,22 +1,18 @@
|
||||
<template>
|
||||
<tfoot>
|
||||
<tr class="text-right">
|
||||
<td colspan="4">Subtotal</td>
|
||||
<td :colspan="colspan">Subtotal</td>
|
||||
<td>{{ invoice.subTotal | currency }}</td>
|
||||
</tr>
|
||||
<tr class="text-right">
|
||||
<td colspan="4">
|
||||
VAT
|
||||
(<AppEditable :value="invoice.vat_rate | currency"
|
||||
suffix="%"
|
||||
placeholder="Add VAT"
|
||||
@change="updateProp({ vat_rate: $event })"/>)
|
||||
<AppError :errors="errors" field="vat_rate"/>
|
||||
<tr class="text-right" v-for="tax in invoice.taxes" :key="tax.label">
|
||||
<td :colspan="colspan">
|
||||
{{ tax.label }}
|
||||
<!--({{ tax.rate | currency }}%)-->
|
||||
</td>
|
||||
<td>{{ invoice.totalVat | currency }}</td>
|
||||
<td>{{ tax.total | currency }}</td>
|
||||
</tr>
|
||||
<tr class="text-right">
|
||||
<th colspan="4">
|
||||
<th :colspan="colspan">
|
||||
Total
|
||||
<AppEditable :value="invoice.currency"
|
||||
:errors="errors"
|
||||
@ -29,7 +25,7 @@
|
||||
</tfoot>
|
||||
</template>
|
||||
<script>
|
||||
import AppError from '@/components/form/AppError';
|
||||
import { mapGetters } from 'vuex';
|
||||
import AppEditable from '../form/AppEditable';
|
||||
import { formatDate } from '../../filters/date.filter';
|
||||
import { formatCurrency } from '../../filters/currency.filter';
|
||||
@ -38,12 +34,19 @@ export default {
|
||||
props: ['invoice', 'errors'],
|
||||
components: {
|
||||
AppEditable,
|
||||
AppError,
|
||||
},
|
||||
filters: {
|
||||
date: formatDate,
|
||||
currency: formatCurrency,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
taxes: 'invoiceRows/taxes',
|
||||
}),
|
||||
colspan() {
|
||||
return 4 + this.taxes.length;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateProp(props) {
|
||||
this.$emit('update', props);
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
<td>{{ invoice.client ? invoice.client.company_name : '' }}</td>
|
||||
<td>{{ invoice.issued_at | date('D MMM YYYY', 'YYYY-MM-DD') }}</td>
|
||||
<td>
|
||||
{{ invoice.total | currency }}
|
||||
<small v-if="invoice.vat_rate"><br>({{ totalWithVat(invoice) | currency }})</small>
|
||||
{{ invoice.subTotal | currency }}
|
||||
<small v-if="invoice.taxTotal"><br>({{ invoice.total | currency }})</small>
|
||||
</td>
|
||||
<td class="text-right text-capitalize">
|
||||
<i class="material-icons material-icons-round md-18 mr-2 text-warning"
|
||||
@ -72,9 +72,6 @@ export default {
|
||||
params: { id: invoice.id },
|
||||
});
|
||||
},
|
||||
totalWithVat(invoice) {
|
||||
return (invoice.vat_rate / 100 * invoice.total) + invoice.total;
|
||||
},
|
||||
isOverDue(invoice) {
|
||||
return invoice.status === 'sent' && invoice.due_at < dayjs()
|
||||
.format();
|
||||
|
||||
@ -32,8 +32,6 @@
|
||||
|
||||
<b-tab title="Invoicing" class="col-12">
|
||||
<div class="row">
|
||||
<AppInput :value="team.vat_rate" @change="updateProp({ vat_rate: $event })" type="number"
|
||||
label="VAT rate" field="vat_rate" :errors="errors" class="col-sm-4"/>
|
||||
<AppInput :value="team.invoice_late_fee" @change="updateProp({ invoice_late_fee: $event })"
|
||||
type="number"
|
||||
label="Late fee (%)" field="invoice_late_fee" :errors="errors" class="col-sm-4"/>
|
||||
|
||||
@ -22,7 +22,6 @@ class InvoiceService {
|
||||
async updateInvoice(invoice) {
|
||||
const requiredFields = {
|
||||
currency: 'Currency',
|
||||
vat_rate: 'Vat Rate',
|
||||
late_fee: 'Late Fee',
|
||||
issued_at: 'Issued At',
|
||||
due_at: 'Due At',
|
||||
@ -47,7 +46,6 @@ class InvoiceService {
|
||||
async bookInvoice(invoice) {
|
||||
const requiredFields = {
|
||||
currency: 'Currency',
|
||||
vat_rate: 'Vat rate',
|
||||
late_fee: 'Late fee',
|
||||
issued_at: 'Issued at',
|
||||
due_at: 'Due at',
|
||||
|
||||
@ -14,7 +14,6 @@ class TeamService {
|
||||
website: null,
|
||||
contact_email: null,
|
||||
contact_phone: null,
|
||||
vat_rate: null,
|
||||
invoice_late_fee: null,
|
||||
invoice_due_days: null,
|
||||
updated_at: null,
|
||||
|
||||
@ -56,7 +56,7 @@ export default {
|
||||
},
|
||||
async updateClient({ dispatch }, payload) {
|
||||
if (payload.props) {
|
||||
await dispatch('clientProps', payload.props);
|
||||
await dispatch('clientProps', payload);
|
||||
}
|
||||
return ClientService.updateClient(getClientById(payload.clientId));
|
||||
},
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import InvoiceRow from '@/store/models/invoice-row';
|
||||
import InvoiceRowTax from '@/store/models/invoice-row-tax';
|
||||
import { flatten, uniqBy } from 'lodash';
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
},
|
||||
mutations: {
|
||||
},
|
||||
state: {},
|
||||
mutations: {},
|
||||
actions: {
|
||||
init() {},
|
||||
terminate() {},
|
||||
init() {
|
||||
},
|
||||
terminate() {
|
||||
},
|
||||
invoiceRowProps(store, payload) {
|
||||
return InvoiceRow.update({
|
||||
where: payload.id,
|
||||
@ -21,16 +23,48 @@ export default {
|
||||
invoiceId: payload.invoiceId,
|
||||
}, { root: true });
|
||||
},
|
||||
async addRow(store, invoiceId) {
|
||||
async addRow({ getters, rootGetters }, invoiceId) {
|
||||
const row = await InvoiceRow.createNew();
|
||||
const rowCount = InvoiceRow.query().where('invoice_id', invoiceId).count();
|
||||
row.$update({
|
||||
await row.$update({
|
||||
invoice_id: invoiceId,
|
||||
order: rowCount,
|
||||
});
|
||||
|
||||
const client = rootGetters['invoices/invoice'].client;
|
||||
if (client && client.has_tax) {
|
||||
const taxes = getters.taxes.length > 0
|
||||
? getters.taxes
|
||||
: rootGetters['taxes/all'];
|
||||
taxes.forEach((tax) => {
|
||||
const rowTax = new InvoiceRowTax();
|
||||
rowTax.label = tax.label;
|
||||
rowTax.value = tax.value;
|
||||
rowTax.row_id = row.id;
|
||||
rowTax.$save();
|
||||
});
|
||||
}
|
||||
},
|
||||
async removeRow(store, rowId) {
|
||||
await InvoiceRow.delete(rowId);
|
||||
},
|
||||
async updateInvoiceRowTax({ dispatch }, payload) {
|
||||
await InvoiceRowTax.update({
|
||||
where: payload.taxId,
|
||||
data: payload.props,
|
||||
});
|
||||
return dispatch('invoices/updateInvoice', {
|
||||
invoiceId: payload.invoiceId,
|
||||
}, { root: true });
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
taxes(state, getters, rootState, rootGetters) {
|
||||
let taxes = rootGetters['invoices/invoice'].rows.map(row => row.taxes);
|
||||
taxes = flatten(taxes);
|
||||
taxes = uniqBy(taxes, 'label');
|
||||
taxes = taxes.filter(tax => !!tax.label);
|
||||
return taxes;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -6,7 +6,7 @@ import Errors from '@/utils/errors';
|
||||
|
||||
function getInvoice(invoiceId) {
|
||||
return Invoice.query()
|
||||
.with(['client', 'client_fields', 'team_fields'])
|
||||
.with(['client', 'client_fields', 'team_fields', 'rows.taxes'])
|
||||
.with('rows', query => query.orderBy('order', 'asc'))
|
||||
.find(invoiceId);
|
||||
}
|
||||
@ -76,9 +76,6 @@ export default {
|
||||
client_email: 'invoice_email',
|
||||
currency: 'currency',
|
||||
});
|
||||
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 && invoice.client_id) {
|
||||
@ -100,7 +97,6 @@ export default {
|
||||
from_website: 'website',
|
||||
from_email: 'contact_email',
|
||||
from_phone: 'contact_phone',
|
||||
vat_rate: 'vat_rate',
|
||||
});
|
||||
const invoice = getInvoice(payload.invoiceId);
|
||||
|
||||
@ -108,13 +104,6 @@ export default {
|
||||
teamProps.invoice_due_days = dayjs(invoice.due_at)
|
||||
.diff(invoice.issued_at, 'days');
|
||||
}
|
||||
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) {
|
||||
delete teamProps.vat_rate;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(teamProps).length > 0) {
|
||||
dispatch('teams/updateTeam', teamProps, { root: true });
|
||||
@ -173,7 +162,6 @@ export default {
|
||||
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,
|
||||
},
|
||||
@ -213,7 +201,6 @@ export default {
|
||||
from_website: team.website,
|
||||
from_email: team.contact_email,
|
||||
from_phone: team.contact_phone,
|
||||
vat_rate: team.vat_rate || 0,
|
||||
currency: team.currency || 'USD',
|
||||
},
|
||||
});
|
||||
@ -226,7 +213,7 @@ export default {
|
||||
all() {
|
||||
return Invoice.query()
|
||||
.where('$isNew', false)
|
||||
.with(['client'])
|
||||
.with(['client', 'rows.taxes'])
|
||||
.with('rows', query => query.orderBy('order', 'asc')) // TODO: do we need this?
|
||||
.orderBy('issued_at', 'desc')
|
||||
.orderBy('number', 'desc')
|
||||
|
||||
@ -16,7 +16,7 @@ export default class Client extends Model {
|
||||
company_country: this.attr(''),
|
||||
company_county: this.attr(''),
|
||||
company_city: this.attr(''),
|
||||
has_vat: this.attr(null),
|
||||
has_tax: this.attr(true),
|
||||
currency: this.attr(null),
|
||||
rate: this.attr(null),
|
||||
invoice_email: this.attr(''),
|
||||
|
||||
16
src/store/models/invoice-row-tax.js
Normal file
16
src/store/models/invoice-row-tax.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { Model } from '@vuex-orm/core';
|
||||
import { uuidv4 } from '@/utils/helpers';
|
||||
|
||||
export default class InvoiceRowTax extends Model {
|
||||
// This is the name used as module name of the Vuex Store.
|
||||
static entity = 'invoice_row_taxes';
|
||||
|
||||
static fields() {
|
||||
return {
|
||||
id: this.attr(() => uuidv4()),
|
||||
row_id: this.attr(null),
|
||||
label: this.attr(''),
|
||||
value: this.attr(''),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import { Model } from '@vuex-orm/core';
|
||||
import { uuidv4 } from '@/utils/helpers';
|
||||
import Invoice from '@/store/models/invoice';
|
||||
import InvoiceRowTax from '@/store/models/invoice-row-tax';
|
||||
|
||||
export default class InvoiceRow extends Model {
|
||||
// This is the name used as module name of the Vuex Store.
|
||||
@ -16,6 +17,7 @@ export default class InvoiceRow extends Model {
|
||||
price: this.attr(null),
|
||||
unit: this.attr(''),
|
||||
order: this.attr(null),
|
||||
taxes: this.hasMany(InvoiceRowTax, 'row_id'),
|
||||
updated_at: this.attr(''),
|
||||
created_at: this.attr(''),
|
||||
};
|
||||
|
||||
@ -17,7 +17,6 @@ export default class Invoice extends Model {
|
||||
issued_at: this.attr(''),
|
||||
due_at: this.attr(''),
|
||||
late_fee: this.attr(''),
|
||||
vat_rate: this.attr(''),
|
||||
currency: this.attr(''),
|
||||
from_name: this.attr(''),
|
||||
from_address: this.attr(''),
|
||||
@ -57,16 +56,36 @@ export default class Invoice extends Model {
|
||||
set subTotal(val) {}
|
||||
|
||||
get total() {
|
||||
return this.subTotal + this.totalVat;
|
||||
return this.subTotal + this.taxTotal;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-empty-function
|
||||
set total(val) {}
|
||||
|
||||
get totalVat() {
|
||||
return (this.vat_rate / 100) * this.subTotal;
|
||||
get taxTotal() {
|
||||
return Object.values(this.taxes).reduce((carr, tax) => (tax.total + carr), 0);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-empty-function
|
||||
set totalVat(val) {}
|
||||
set taxTotal(val) {}
|
||||
|
||||
get taxes() {
|
||||
const taxes = {};
|
||||
|
||||
this.rows.forEach(row => row.taxes.forEach((tax) => {
|
||||
if (!taxes.hasOwnProperty(tax.label)) {
|
||||
taxes[tax.label] = {
|
||||
total: 0,
|
||||
label: tax.label,
|
||||
rate: tax.value,
|
||||
};
|
||||
}
|
||||
taxes[tax.label].total += (row.quantity * row.price) * tax.value / 100;
|
||||
}));
|
||||
|
||||
return taxes;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-empty-function
|
||||
set taxes(val) {}
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ export default class Team extends Model {
|
||||
website: this.attr(''),
|
||||
contact_email: this.attr(''),
|
||||
contact_phone: this.attr(''),
|
||||
vat_rate: this.attr(null),
|
||||
currency: this.attr(null),
|
||||
invoice_late_fee: this.attr(null),
|
||||
invoice_due_days: this.attr(null),
|
||||
|
||||
@ -24,6 +24,7 @@ import TeamField from '@/store/models/team-field';
|
||||
import InvoiceClientField from '@/store/models/invoice-client-field';
|
||||
import InvoiceTeamField from '@/store/models/invoice-team-field';
|
||||
import Tax from '@/store/models/tax';
|
||||
import InvoiceRowTax from '@/store/models/invoice-row-tax';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
@ -39,6 +40,7 @@ database.register(Invoice);
|
||||
database.register(InvoiceClientField);
|
||||
database.register(InvoiceTeamField);
|
||||
database.register(InvoiceRow);
|
||||
database.register(InvoiceRowTax);
|
||||
database.register(BankAccount);
|
||||
|
||||
export default new Vuex.Store({
|
||||
|
||||
Reference in New Issue
Block a user