mirror of
https://github.com/mokuappio/serverless-invoices.git
synced 2025-10-28 00:11:08 -04:00
Be able to add default taxes to team.
This commit is contained in:
@ -34,9 +34,11 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<AppInput :value="team.vat_rate" @change="updateProp({ vat_rate: $event })" type="number"
|
<AppInput :value="team.vat_rate" @change="updateProp({ vat_rate: $event })" type="number"
|
||||||
label="VAT rate" field="vat_rate" :errors="errors" class="col-sm-4"/>
|
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"
|
<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"/>
|
label="Late fee (%)" field="invoice_late_fee" :errors="errors" class="col-sm-4"/>
|
||||||
<AppInput :value="team.invoice_due_days" @change="updateProp({ invoice_due_days: $event })" type="number"
|
<AppInput :value="team.invoice_due_days" @change="updateProp({ invoice_due_days: $event })"
|
||||||
|
type="number"
|
||||||
label="Payment terms, days" field="invoice_due_days" :errors="errors" class="col-sm-4"/>
|
label="Payment terms, days" field="invoice_due_days" :errors="errors" class="col-sm-4"/>
|
||||||
<AppInput :value="team.currency" @change="updateProp({ currency: $event })"
|
<AppInput :value="team.currency" @change="updateProp({ currency: $event })"
|
||||||
label="Default currency" field="currency" :errors="errors" class="col-sm-4"/>
|
label="Default currency" field="currency" :errors="errors" class="col-sm-4"/>
|
||||||
@ -61,6 +63,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
|
|
||||||
|
<b-tab title="Taxes" class="col-12">
|
||||||
|
<TeamTaxes class="row"/>
|
||||||
|
</b-tab>
|
||||||
|
|
||||||
</b-tabs>
|
</b-tabs>
|
||||||
|
|
||||||
<div v-if="!team">Loading</div>
|
<div v-if="!team">Loading</div>
|
||||||
@ -76,11 +82,13 @@ import AppInput from '@/components/form/AppInput';
|
|||||||
import Errors from '@/utils/errors';
|
import Errors from '@/utils/errors';
|
||||||
import TeamFields from '@/components/team/TeamFields';
|
import TeamFields from '@/components/team/TeamFields';
|
||||||
import TeamLogo from '@/components/team/TeamLogo';
|
import TeamLogo from '@/components/team/TeamLogo';
|
||||||
|
import TeamTaxes from '@/components/team/TeamTaxes';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
TeamLogo,
|
TeamLogo,
|
||||||
TeamFields,
|
TeamFields,
|
||||||
|
TeamTaxes,
|
||||||
AppInput,
|
AppInput,
|
||||||
BTab,
|
BTab,
|
||||||
BTabs,
|
BTabs,
|
||||||
|
|||||||
64
src/components/team/TeamTaxes.vue
Normal file
64
src/components/team/TeamTaxes.vue
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-for="tax in taxes" :key="tax.id" class="col-sm-6">
|
||||||
|
<AppEditable :value="tax.label"
|
||||||
|
placeholder="Label"
|
||||||
|
@change="updateTaxProp({ label: $event }, tax)"/>
|
||||||
|
<i class="material-icons md-18 float-right pointer" @click="removeTax(tax)">close</i>
|
||||||
|
<AppInput :value="tax.value" @change="updateTaxProp({ value: $event }, tax)"
|
||||||
|
:placeholder="tax.label"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<button class="btn btn-sm btn-secondary " @click="addNewTax">
|
||||||
|
<i class="material-icons md-18">add</i>
|
||||||
|
Tax
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import NotificationService from '@/services/notification.service';
|
||||||
|
import AppInput from '@/components/form/AppInput';
|
||||||
|
import AppEditable from '@/components/form/AppEditable';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
AppEditable,
|
||||||
|
AppInput,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
taxes: 'taxes/all',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addNewTax() {
|
||||||
|
this.$store.dispatch('taxes/addNewTax');
|
||||||
|
},
|
||||||
|
async removeTax(field) {
|
||||||
|
const confirmed = await this.$bvModal.msgBoxConfirm(`Delete tax ${field.label}?`, {
|
||||||
|
okTitle: 'Delete',
|
||||||
|
okVariant: 'danger',
|
||||||
|
cancelTitle: 'Dismiss',
|
||||||
|
cancelVariant: 'btn-link',
|
||||||
|
contentClass: 'bg-base dp--24',
|
||||||
|
});
|
||||||
|
if (confirmed) {
|
||||||
|
await this.$store.dispatch('taxes/deleteTax', field.id);
|
||||||
|
try {
|
||||||
|
NotificationService.success('Deleted');
|
||||||
|
} catch (err) {
|
||||||
|
NotificationService.error(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateTaxProp(props, tax) {
|
||||||
|
this.$store.dispatch('taxes/updateTax', {
|
||||||
|
props,
|
||||||
|
taxId: tax.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
45
src/services/tax.service.js
Normal file
45
src/services/tax.service.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import storage from 'localforage';
|
||||||
|
import { removeVuexORMFlags } from '@/utils/helpers';
|
||||||
|
|
||||||
|
class TaxService {
|
||||||
|
async getTaxes() {
|
||||||
|
const taxes = await storage.getItem('taxes');
|
||||||
|
|
||||||
|
return taxes || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTax(taxId) {
|
||||||
|
const taxes = await this.getTaxes();
|
||||||
|
return taxes.find(tax => tax.id === taxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTax(tax) {
|
||||||
|
return this.saveTax(tax);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateTax(tax) {
|
||||||
|
return this.saveTax(tax);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteTax(taxId) {
|
||||||
|
const taxes = await this.getTaxes();
|
||||||
|
const index = taxes.findIndex(item => item.id === taxId);
|
||||||
|
taxes.splice(index, 1);
|
||||||
|
return storage.setItem('taxes', taxes);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveTax(tax) {
|
||||||
|
const taxes = await this.getTaxes();
|
||||||
|
const index = taxes.findIndex(item => item.id === tax.id);
|
||||||
|
removeVuexORMFlags(tax);
|
||||||
|
if (index === -1) {
|
||||||
|
taxes.push(tax);
|
||||||
|
} else {
|
||||||
|
taxes[index] = tax;
|
||||||
|
}
|
||||||
|
await storage.setItem('taxes', taxes);
|
||||||
|
return tax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TaxService();
|
||||||
15
src/store/models/tax.js
Normal file
15
src/store/models/tax.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Model } from '@vuex-orm/core';
|
||||||
|
import { uuidv4 } from '@/utils/helpers';
|
||||||
|
|
||||||
|
export default class Tax extends Model {
|
||||||
|
// This is the name used as module name of the Vuex Store.
|
||||||
|
static entity = 'taxes';
|
||||||
|
|
||||||
|
static fields() {
|
||||||
|
return {
|
||||||
|
id: this.attr(() => uuidv4()),
|
||||||
|
label: this.attr(''),
|
||||||
|
value: this.attr(''),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,17 +17,20 @@ import invoiceTeamFields from '@/store/invoice-team-fields';
|
|||||||
import teams from '@/store/teams';
|
import teams from '@/store/teams';
|
||||||
import teamFields from '@/store/team-fields';
|
import teamFields from '@/store/team-fields';
|
||||||
import themes from '@/store/themes';
|
import themes from '@/store/themes';
|
||||||
|
import taxes from '@/store/taxes';
|
||||||
import data from '@/store/data';
|
import data from '@/store/data';
|
||||||
import ClientField from '@/store/models/client-field';
|
import ClientField from '@/store/models/client-field';
|
||||||
import TeamField from '@/store/models/team-field';
|
import TeamField from '@/store/models/team-field';
|
||||||
import InvoiceClientField from '@/store/models/invoice-client-field';
|
import InvoiceClientField from '@/store/models/invoice-client-field';
|
||||||
import InvoiceTeamField from '@/store/models/invoice-team-field';
|
import InvoiceTeamField from '@/store/models/invoice-team-field';
|
||||||
|
import Tax from '@/store/models/tax';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
VuexORM.use(VuexORMisDirtyPlugin);
|
VuexORM.use(VuexORMisDirtyPlugin);
|
||||||
const database = new VuexORM.Database();
|
const database = new VuexORM.Database();
|
||||||
|
|
||||||
|
database.register(Tax);
|
||||||
database.register(Team);
|
database.register(Team);
|
||||||
database.register(TeamField);
|
database.register(TeamField);
|
||||||
database.register(Client);
|
database.register(Client);
|
||||||
@ -51,6 +54,7 @@ export default new Vuex.Store({
|
|||||||
teams,
|
teams,
|
||||||
teamFields,
|
teamFields,
|
||||||
themes,
|
themes,
|
||||||
|
taxes,
|
||||||
data,
|
data,
|
||||||
},
|
},
|
||||||
state: {},
|
state: {},
|
||||||
|
|||||||
44
src/store/taxes.js
Normal file
44
src/store/taxes.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import Tax from '@/store/models/tax';
|
||||||
|
import TaxService from '@/services/tax.service';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: {},
|
||||||
|
mutations: {},
|
||||||
|
actions: {
|
||||||
|
init({ dispatch }) {
|
||||||
|
return dispatch('getTaxes');
|
||||||
|
},
|
||||||
|
terminate() {
|
||||||
|
return Tax.deleteAll();
|
||||||
|
},
|
||||||
|
async getTaxes() {
|
||||||
|
const taxes = await TaxService.getTaxes();
|
||||||
|
await Tax.create({ data: taxes });
|
||||||
|
return taxes;
|
||||||
|
},
|
||||||
|
async taxProps(store, payload) {
|
||||||
|
return Tax.update({
|
||||||
|
where: payload.taxId,
|
||||||
|
data: payload.props,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async updateTax({ dispatch }, payload) {
|
||||||
|
const tax = await dispatch('taxProps', payload);
|
||||||
|
return TaxService.updateTax(tax);
|
||||||
|
},
|
||||||
|
async addNewTax() {
|
||||||
|
const tax = await Tax.createNew();
|
||||||
|
return TaxService.createTax(tax);
|
||||||
|
},
|
||||||
|
async deleteTax(store, taxId) {
|
||||||
|
await Tax.delete(taxId);
|
||||||
|
return TaxService.deleteTax(taxId);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
all() {
|
||||||
|
return Tax.all();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -17,6 +17,7 @@ export default {
|
|||||||
dispatch('clients/terminate', null, { root: true }),
|
dispatch('clients/terminate', null, { root: true }),
|
||||||
dispatch('bankAccounts/terminate', null, { root: true }),
|
dispatch('bankAccounts/terminate', null, { root: true }),
|
||||||
dispatch('invoices/terminate', null, { root: true }),
|
dispatch('invoices/terminate', null, { root: true }),
|
||||||
|
dispatch('taxes/terminate', null, { root: true }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await dispatch('getTeam');
|
await dispatch('getTeam');
|
||||||
@ -24,6 +25,7 @@ export default {
|
|||||||
dispatch('clients/init', null, { root: true });
|
dispatch('clients/init', null, { root: true });
|
||||||
dispatch('bankAccounts/init', null, { root: true });
|
dispatch('bankAccounts/init', null, { root: true });
|
||||||
dispatch('invoices/init', null, { root: true });
|
dispatch('invoices/init', null, { root: true });
|
||||||
|
dispatch('taxes/init', null, { root: true });
|
||||||
},
|
},
|
||||||
async terminate() {
|
async terminate() {
|
||||||
return Team.deleteAll();
|
return Team.deleteAll();
|
||||||
|
|||||||
Reference in New Issue
Block a user