Init commit

This commit is contained in:
Marek Fraczyk
2021-02-16 16:24:22 +02:00
parent 056a817632
commit 79e9705b01
106 changed files with 18400 additions and 0 deletions

View File

@ -0,0 +1,67 @@
import BankAccountService from '@/services/bank-account.service';
import BankAccount from '@/store/models/bank-account';
export default {
namespaced: true,
state: {
bankAccountId: null,
isModalOpen: null,
},
mutations: {
bankAccountId(state, bankAccountId) {
state.bankAccountId = bankAccountId;
},
isModalOpen(state, isOpen) {
state.isModalOpen = isOpen;
},
},
actions: {
init({ dispatch }) {
return dispatch('getBankAccounts');
},
terminate() {
return BankAccount.deleteAll();
},
async getBankAccounts() {
const accounts = await BankAccountService.getBankAccounts();
await BankAccount.create({ data: accounts });
return accounts;
},
async getBankAccount({ commit }, bankAccountId) {
const bankAccount = await BankAccountService.getBankAccount(bankAccountId);
commit('bankAccountId', bankAccount.id);
return BankAccount.insert({ data: bankAccount });
},
async createNewBankAccount(store, bankAccount) {
const res = await BankAccountService.createBankAccount(bankAccount);
await BankAccount.insert({ data: res });
return BankAccount.find(res.id);
},
bankAccountProps({ state }, props) {
return BankAccount.update({
where: state.bankAccountId,
data: props,
});
},
async updateBankAccount({ getters, dispatch }, props) {
await dispatch('bankAccountProps', props);
return BankAccountService.updateBankAccount(getters.bankAccount);
},
async openNewBankAccountModal({ commit }) {
const bankAccount = await BankAccount.createNew();
commit('bankAccountId', bankAccount.id);
commit('isModalOpen', true);
},
},
getters: {
bankAccount(state) {
return BankAccount.query()
.find(state.bankAccountId);
},
all() {
return BankAccount.query()
.where('$isNew', false)
.get();
},
},
};

86
src/store/clients.js Normal file
View File

@ -0,0 +1,86 @@
import ClientService from '@/services/client.service';
import Client from '@/store/models/client';
export default {
namespaced: true,
state: {
clientId: null,
isModalOpen: false,
},
mutations: {
clientId(state, clientId) {
state.clientId = clientId;
},
isModalOpen(state, isOpen) {
state.isModalOpen = isOpen;
},
},
actions: {
init({ dispatch }) {
return dispatch('getClients');
},
terminate() {
return Client.deleteAll();
},
async getClients() {
const clients = await ClientService.getClients();
await Client.create({ data: clients });
return clients;
},
async getClient({ commit }, clientId) {
const client = await ClientService.getClient(clientId);
commit('clientId', client.id);
Client.insert({ data: client });
},
async createNewClient(store, client) {
if (!client.hasOwnProperty('id')) {
client = new Client(client);
}
const res = await ClientService.createClient(client);
await Client.insert({ data: res });
return Client.find(res.id);
},
clientProps({ state }, props) {
return Client.update({
where: state.clientId,
data: props,
});
},
async updateClient({ getters, dispatch }, props) {
await dispatch('clientProps', props);
return ClientService.updateClient(getters.client);
},
async updateClientById(payload) {
const client = await Client.update({
where: payload.clientId,
data: payload.props,
});
return ClientService.updateClient(client);
},
async openNewClientModal({ commit }) {
const client = await Client.createNew();
commit('clientId', client.id);
commit('isModalOpen', true);
},
async deleteClient(clientId) {
const res = await ClientService.deleteClient(clientId);
if ('client_id' in res) {
Client.delete(res.client_id);
}
return res;
},
},
getters: {
client(state) {
return Client.query()
.with(['bank_account'])
.find(state.clientId);
},
all() {
return Client.query()
.where('$isNew', false)
.with(['bank_account'])
.get();
},
},
};

197
src/store/invoices.js Normal file
View File

@ -0,0 +1,197 @@
import InvoiceService from '@/services/invoice.service';
import Invoice from '@/store/models/invoice';
import InvoiceRow from '@/store/models/invoice-row';
import { pick } from '@/utils/helpers';
import dayjs from 'dayjs';
import Errors from '@/utils/errors';
export default {
namespaced: true,
state: {
errors: new Errors(),
invoiceId: null,
isSendModalOpen: false,
},
mutations: {
invoiceId(state, invoiceId) {
state.invoiceId = invoiceId;
},
isSendModalOpen(state, isSendModalOpen) {
state.isSendModalOpen = isSendModalOpen;
},
setErrors(state, errors) {
state.errors.set(errors);
},
clearErrors(state) {
state.errors.clear();
},
},
actions: {
init({ dispatch }) {
dispatch('getInvoices');
},
terminate() {
return Invoice.deleteAll();
},
async getInvoices() {
const invoices = await InvoiceService.getInvoices();
await Invoice.create({ data: invoices });
return invoices;
},
async getInvoice({ commit }, invoiceId) {
const invoice = await InvoiceService.getInvoice(invoiceId);
await Invoice.insert({ data: invoice });
commit('invoiceId', invoiceId);
return invoice;
},
async createNewInvoice() {
const invoice = await Invoice.createNew();
await InvoiceService.createInvoice(invoice);
return invoice.id;
},
invoiceProps({ state }, props) {
return Invoice.update({
where: state.invoiceId,
data: props,
});
},
invoiceRowProps(store, payload) {
return InvoiceRow.update({
where: payload.id,
data: payload.props,
});
},
async updateInvoice({ getters, dispatch, commit }, props) {
await dispatch('invoiceProps', props);
// Update client
const clientProps = pick(props, {
bank_account_id: 'bank_account_id',
client_name: 'company_name',
client_address: 'company_address',
client_postal_code: 'company_postal_code',
client_country: 'company_country',
client_county: 'company_county',
client_city: 'company_city',
client_reg_no: 'company_reg_no',
client_vat_no: 'company_vat_no',
client_email: 'invoice_email',
currency: 'currency',
});
if ('vat_rate' in props) {
clientProps.has_vat = props.vat_rate > 0;
}
if (Object.keys(clientProps).length > 0 && getters.invoice.client_id) {
dispatch('clients/updateClientById', {
props: clientProps,
clientId: getters.invoice.client_id,
}, { root: true });
}
const teamProps = pick(props, {
late_fee: 'invoice_late_fee',
from_name: 'company_name',
from_address: 'company_address',
from_postal_code: 'company_postal_code',
from_city: 'company_city',
from_country: 'company_country',
from_county: 'company_county',
from_reg_no: 'company_reg_no',
from_vat_no: 'company_vat_no',
from_website: 'website',
from_email: 'contact_email',
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');
}
if ('vat_rate' in 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 });
}
commit('clearErrors');
return InvoiceService.updateInvoice(getters.invoice)
.catch(err => commit('setErrors', err.response.data.errors));
},
async updateInvoiceRow({ getters, dispatch, commit }, payload) {
await dispatch('invoiceRowProps', payload);
commit('clearErrors');
return InvoiceService.updateInvoice(getters.invoice)
.catch(err => commit('setErrors', err.response.data.errors));
},
async deleteInvoice(invoice) {
const res = await InvoiceService.deleteInvoice(invoice.id);
if ('invoice_id' in res) {
Invoice.delete(res.invoice_id);
}
return res;
},
async addRow({ state, getters }) {
const row = await InvoiceRow.createNew();
row.$update({
invoice_id: state.invoiceId,
order: getters.invoice.rows.length,
});
},
async removeRow(store, row) {
await InvoiceRow.delete(row.id);
},
async sendInvoice({ state, dispatch }, message) {
const res = await InvoiceService.sendInvoice(state.invoiceId, message);
dispatch('invoiceProps', {
status: 'sent',
});
return res;
},
async bookInvoice({ getters, commit, dispatch }) {
commit('clearErrors');
try {
const res = await InvoiceService.bookInvoice(getters.invoice);
return dispatch('getInvoice', res.invoice_id);
} catch (err) {
commit('setErrors', err.response.data.errors);
}
},
},
getters: {
invoice(state) {
return Invoice.query()
.with(['client', 'project', 'team.logos'])
.with('rows', query => query.orderBy('order', 'asc'))
.find(state.invoiceId);
},
all() {
return Invoice.query()
.where('$isNew', false)
.with(['client'])
.with('rows', query => query.orderBy('order', 'asc')) // TODO: do we need this?
.orderBy('issued_at', 'desc')
.orderBy('number', 'desc')
.get();
},
subTotal(state, getters) {
return getters.invoice.rows.reduce((carr, row) => (row.quantity * row.price) + carr, 0);
},
total(state, getters) {
return getters.subTotal + getters.totalVat;
},
totalVat(state, getters) {
return (getters.invoice.vat_rate / 100) * getters.subTotal;
},
},
};

View File

@ -0,0 +1,17 @@
import { Model } from '@vuex-orm/core';
import { uuidv4 } from '@/utils/helpers';
export default class BankAccount extends Model {
// This is the name used as module name of the Vuex Store.
static entity = 'bank_accounts';
static fields() {
return {
id: this.attr(() => uuidv4()),
account_no: this.attr(''),
bank_name: this.attr(''),
updated_at: this.attr(''),
created_at: this.attr(''),
};
}
}

View File

@ -0,0 +1,30 @@
import { Model } from '@vuex-orm/core';
import { uuidv4 } from '@/utils/helpers';
import BankAccount from '@/store/models/bank-account';
export default class Client extends Model {
// This is the name used as module name of the Vuex Store.
static entity = 'clients';
static fields() {
return {
id: this.attr(() => uuidv4()),
company_name: this.attr(''),
company_address: this.attr(''),
company_postal_code: this.attr(''),
company_country: this.attr(''),
company_county: this.attr(''),
company_city: this.attr(''),
company_reg_no: this.attr(''),
company_vat_no: this.attr(''),
has_vat: this.attr(null),
currency: this.attr(null),
rate: this.attr(null),
invoice_email: this.attr(''),
bank_account_id: this.attr(null),
bank_account: this.belongsTo(BankAccount, 'bank_account_id', 'id'),
updated_at: this.attr(''),
created_at: this.attr(''),
};
}
}

View File

@ -0,0 +1,23 @@
import { Model } from '@vuex-orm/core';
import { uuidv4 } from '@/utils/helpers';
import Invoice from '@/store/models/invoice';
export default class InvoiceRow extends Model {
// This is the name used as module name of the Vuex Store.
static entity = 'invoice_rows';
static fields() {
return {
id: this.attr(() => uuidv4()),
invoice_id: this.attr(null),
invoice: this.belongsTo(Invoice, 'invoice_id'),
item: this.attr(''),
quantity: this.attr(null),
price: this.attr(null),
unit: this.attr(''),
order: this.attr(null),
updated_at: this.attr(''),
created_at: this.attr(''),
};
}
}

View File

@ -0,0 +1,51 @@
import { Model } from '@vuex-orm/core';
import { uuidv4 } from '@/utils/helpers';
import Client from '@/store/models/client';
import InvoiceRow from '@/store/models/invoice-row';
export default class Invoice extends Model {
// This is the name used as module name of the Vuex Store.
static entity = 'invoices';
static fields() {
return {
id: this.attr(() => uuidv4()),
number: this.attr(''),
status: this.attr('draft'),
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(''),
from_postal_code: this.attr(''),
from_city: this.attr(''),
from_country: this.attr(''),
from_county: this.attr(''),
from_reg_no: this.attr(''),
from_vat_no: this.attr(''),
from_website: this.attr(''),
from_email: this.attr(''),
from_phone: this.attr(''),
bank_name: this.attr(''),
bank_account_no: this.attr(''),
client_name: this.attr(''),
client_address: this.attr(''),
client_postal_code: this.attr(''),
client_country: this.attr(''),
client_county: this.attr(''),
client_city: this.attr(''),
client_reg_no: this.attr(''),
client_vat_no: this.attr(''),
client_email: this.attr(''),
client_id: this.attr(null),
client: this.belongsTo(Client, 'client_id'),
rows: this.hasMany(InvoiceRow, 'invoice_id'),
notes: this.attr(''),
updated_at: this.attr(''),
created_at: this.attr(''),
total: this.attr(null), // Only used in lists.
};
}
}

30
src/store/models/team.js Normal file
View File

@ -0,0 +1,30 @@
import { Model } from '@vuex-orm/core';
import { uuidv4 } from '@/utils/helpers';
export default class Team extends Model {
// This is the name used as module name of the Vuex Store.
static entity = 'teams';
static fields() {
return {
id: this.attr(() => uuidv4()),
company_name: this.attr(''),
company_address: this.attr(''),
company_postal_code: this.attr(''),
company_country: this.attr(''),
company_county: this.attr(''),
company_city: this.attr(''),
company_reg_no: this.attr(''),
company_vat_no: this.attr(''),
website: this.attr(''),
contact_email: this.attr(''),
contact_phone: this.attr(''),
vat_rate: this.attr(null),
invoice_late_fee: this.attr(null),
invoice_due_days: this.attr(null),
updated_at: this.attr(''),
created_at: this.attr(''),
logo_url: this.attr(''),
};
}
}

39
src/store/store.js Normal file
View File

@ -0,0 +1,39 @@
import Vue from 'vue';
import Vuex from 'vuex';
import VuexORM from '@vuex-orm/core';
import VuexORMisDirtyPlugin from '@vuex-orm/plugin-change-flags';
import BankAccount from '@/store/models/bank-account';
import Client from '@/store/models/client';
import Invoice from '@/store/models/invoice';
import InvoiceRow from '@/store/models/invoice-row';
import Team from '@/store/models/team';
import bankAccounts from '@/store/bank-accounts';
import clients from '@/store/clients';
import invoices from '@/store/invoices';
import teams from '@/store/teams';
import themes from '@/store/themes';
Vue.use(Vuex);
VuexORM.use(VuexORMisDirtyPlugin);
const database = new VuexORM.Database();
database.register(Team);
database.register(Client);
database.register(Invoice);
database.register(InvoiceRow);
database.register(BankAccount);
export default new Vuex.Store({
plugins: [VuexORM.install(database)],
modules: {
bankAccounts,
clients,
invoices,
teams,
themes,
},
state: {},
mutations: {},
actions: {},
});

49
src/store/teams.js Normal file
View File

@ -0,0 +1,49 @@
import TeamService from '@/services/team.service';
import Team from '@/store/models/team';
export default {
namespaced: true,
state: {},
mutations: {},
actions: {
async init({ dispatch }) {
dispatch('clients/terminate', null, { root: true });
dispatch('bankAccounts/terminate', null, { root: true });
dispatch('invoices/terminate', null, { root: true });
await dispatch('getTeam');
dispatch('clients/init', null, { root: true });
dispatch('bankAccounts/init', null, { root: true });
dispatch('invoices/init', null, { root: true });
},
async getTeam() {
const team = await TeamService.getTeam();
await Team.create({ data: team });
return team;
},
async teamProps({ state }, props) {
return Team.update({
where: state.teamId,
data: props,
});
},
async updateTeam({ getters, dispatch }, props) {
if (props) {
await dispatch('teamProps', props);
}
return TeamService.updateTeam(getters.team);
},
},
getters: {
team() {
return Team.query().first();
},
all() {
return Team.query()
.with(['logos'])
.where('$isNew', false)
.get();
},
},
};

11
src/store/themes.js Normal file
View File

@ -0,0 +1,11 @@
export default {
namespaced: true,
state: {
theme: 'light',
},
mutations: {
theme(state, theme) {
state.theme = theme;
},
},
};