mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-07 13:41:23 +00:00
* refactor: add HTTP client wrapper and upgrade axios to v1 Introduce a thin HTTP wrapper (resources/scripts/http) that centralizes axios configuration, interceptors, and auth header injection. All 43 files now import from the wrapper instead of axios directly, making future library swaps a single-file change. Upgrade axios from 0.30.0 to 1.14.0. * fix: restore window.Ls assignment removed during axios refactor company.js uses window.Ls.set() to persist selected company, which broke after the axios plugin (that set window.Ls) was deleted.
517 lines
15 KiB
JavaScript
Vendored
517 lines
15 KiB
JavaScript
Vendored
import { defineStore } from 'pinia'
|
|
import http from '@/scripts/http'
|
|
import recurringInvoiceStub from '@/scripts/admin/stub/recurring-invoice'
|
|
import recurringInvoiceItemStub from '@/scripts/admin/stub/recurring-invoice-item'
|
|
import TaxStub from '../stub/tax'
|
|
import { useRoute } from 'vue-router'
|
|
import { useCompanyStore } from './company'
|
|
import { useItemStore } from './item'
|
|
import { useTaxTypeStore } from './tax-type'
|
|
import { useCustomerStore } from './customer'
|
|
import Guid from 'guid'
|
|
import { handleError } from '@/scripts/helpers/error-handling'
|
|
import moment from 'moment'
|
|
import _ from 'lodash'
|
|
import { useInvoiceStore } from './invoice'
|
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
|
import { useNotesStore } from './note'
|
|
|
|
export const useRecurringInvoiceStore = (useWindow = false) => {
|
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
|
const { global } = window.i18n
|
|
|
|
return defineStoreFunc({
|
|
id: 'recurring-invoice',
|
|
|
|
state: () => ({
|
|
templates: [],
|
|
recurringInvoices: [],
|
|
selectedRecurringInvoices: [],
|
|
totalRecurringInvoices: 0,
|
|
isFetchingInitialSettings: false,
|
|
isFetchingViewData: false,
|
|
showExchangeRate: false,
|
|
selectAllField: false,
|
|
newRecurringInvoice: {
|
|
...recurringInvoiceStub(),
|
|
},
|
|
|
|
frequencies: [
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_minute'),
|
|
value: '* * * * *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_30_minute'),
|
|
value: '*/30 * * * *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_hour'),
|
|
value: '0 * * * *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_2_hour'),
|
|
value: '0 */2 * * *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_day_at_midnight'),
|
|
value: '0 0 * * *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_week'),
|
|
value: '0 0 * * 0',
|
|
},
|
|
{
|
|
label: global.t(
|
|
'recurring_invoices.frequency.every_15_days_at_midnight',
|
|
),
|
|
value: '0 5 */15 * *',
|
|
},
|
|
{
|
|
label: global.t(
|
|
'recurring_invoices.frequency.on_the_first_day_of_every_month_at_midnight',
|
|
),
|
|
value: '0 0 1 * *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.every_6_month'),
|
|
value: '0 0 1 */6 *',
|
|
},
|
|
{
|
|
label: global.t(
|
|
'recurring_invoices.frequency.every_year_on_the_first_day_of_january_at_midnight',
|
|
),
|
|
value: '0 0 1 1 *',
|
|
},
|
|
{
|
|
label: global.t('recurring_invoices.frequency.custom'),
|
|
value: 'CUSTOM',
|
|
},
|
|
],
|
|
}),
|
|
|
|
getters: {
|
|
getSubTotal() {
|
|
return (
|
|
this.newRecurringInvoice?.items.reduce(function (a, b) {
|
|
return a + b['total']
|
|
}, 0) || 0
|
|
)
|
|
},
|
|
|
|
getNetTotal() {
|
|
return this.getSubtotalWithDiscount - this.getTotalTax
|
|
},
|
|
|
|
getTotalSimpleTax() {
|
|
return _.sumBy(this.newRecurringInvoice.taxes, function (tax) {
|
|
if (!tax.compound_tax) {
|
|
return tax.amount
|
|
}
|
|
return 0
|
|
})
|
|
},
|
|
|
|
getTotalCompoundTax() {
|
|
return _.sumBy(this.newRecurringInvoice.taxes, function (tax) {
|
|
if (tax.compound_tax) {
|
|
return tax.amount
|
|
}
|
|
return 0
|
|
})
|
|
},
|
|
|
|
getTotalTax() {
|
|
if (
|
|
this.newRecurringInvoice.tax_per_item === 'NO' ||
|
|
this.newRecurringInvoice.tax_per_item === null
|
|
) {
|
|
return this.getTotalSimpleTax + this.getTotalCompoundTax
|
|
}
|
|
return _.sumBy(this.newRecurringInvoice.items, function (tax) {
|
|
return tax.tax
|
|
})
|
|
},
|
|
|
|
getSubtotalWithDiscount() {
|
|
return this.getSubTotal - this.newRecurringInvoice.discount_val
|
|
},
|
|
|
|
getTotal() {
|
|
if (this.newRecurringInvoice.tax_included) {
|
|
return this.getSubtotalWithDiscount
|
|
}
|
|
return this.getSubtotalWithDiscount + this.getTotalTax
|
|
},
|
|
},
|
|
|
|
actions: {
|
|
resetCurrentRecurringInvoice() {
|
|
this.newRecurringInvoice = {
|
|
...recurringInvoiceStub(),
|
|
}
|
|
},
|
|
|
|
deselectItem(index) {
|
|
this.newRecurringInvoice.items[index] = {
|
|
...recurringInvoiceItemStub,
|
|
id: Guid.raw(),
|
|
taxes: [{ ...TaxStub, id: Guid.raw() }],
|
|
}
|
|
},
|
|
|
|
addRecurringInvoice(data) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.post('/api/v1/recurring-invoices', data)
|
|
.then((response) => {
|
|
this.recurringInvoices = [
|
|
...this.recurringInvoices,
|
|
response.data.recurringInvoice,
|
|
]
|
|
const notificationStore = useNotificationStore()
|
|
|
|
notificationStore.showNotification({
|
|
type: 'success',
|
|
message: global.t('recurring_invoices.created_message'),
|
|
})
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
fetchRecurringInvoice(id) {
|
|
return new Promise((resolve, reject) => {
|
|
this.isFetchingViewData = true
|
|
http
|
|
.get(`/api/v1/recurring-invoices/${id}`)
|
|
.then((response) => {
|
|
Object.assign(this.newRecurringInvoice, response.data.data)
|
|
this.newRecurringInvoice.invoices =
|
|
response.data.data.invoices || []
|
|
this.setSelectedFrequency()
|
|
this.isFetchingViewData = false
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
this.isFetchingViewData = false
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
updateRecurringInvoice(data) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.put(`/api/v1/recurring-invoices/${data.id}`, data)
|
|
.then((response) => {
|
|
resolve(response)
|
|
|
|
const notificationStore = useNotificationStore()
|
|
|
|
notificationStore.showNotification({
|
|
type: 'success',
|
|
message: global.t('recurring_invoices.updated_message'),
|
|
})
|
|
|
|
let pos = this.recurringInvoices.findIndex(
|
|
(invoice) => invoice.id === response.data.data.id,
|
|
)
|
|
|
|
this.recurringInvoices[pos] = response.data.data
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
selectCustomer(id) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.get(`/api/v1/customers/${id}`)
|
|
.then((response) => {
|
|
this.newRecurringInvoice.customer = response.data.data
|
|
this.newRecurringInvoice.customer_id = response.data.data.id
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
searchRecurringInvoice(data) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.get(`/api/v1/recurring-invoices?${data}`)
|
|
.then((response) => {
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
fetchRecurringInvoices(params) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.get(`/api/v1/recurring-invoices`, { params })
|
|
.then((response) => {
|
|
this.recurringInvoices = response.data.data
|
|
this.totalRecurringInvoices =
|
|
response.data.meta.recurring_invoice_total_count
|
|
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
deleteRecurringInvoice(id) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.post(`/api/v1/recurring-invoices/delete`, id)
|
|
.then((response) => {
|
|
let index = this.recurringInvoices.findIndex(
|
|
(invoice) => invoice.id === id,
|
|
)
|
|
this.recurringInvoices.splice(index, 1)
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
deleteMultipleRecurringInvoices(id) {
|
|
return new Promise((resolve, reject) => {
|
|
let ids = this.selectedRecurringInvoices
|
|
if (id) {
|
|
ids = [id]
|
|
}
|
|
http
|
|
.post(`/api/v1/recurring-invoices/delete`, {
|
|
ids: ids,
|
|
})
|
|
.then((response) => {
|
|
this.selectedRecurringInvoices.forEach((invoice) => {
|
|
let index = this.recurringInvoices.findIndex(
|
|
(_inv) => _inv.id === invoice.id,
|
|
)
|
|
this.recurringInvoices.splice(index, 1)
|
|
})
|
|
this.selectedRecurringInvoices = []
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
handleError(err)
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
|
|
resetSelectedCustomer() {
|
|
this.newRecurringInvoice.customer = null
|
|
this.newRecurringInvoice.customer_id = ''
|
|
},
|
|
|
|
selectRecurringInvoice(data) {
|
|
this.selectedRecurringInvoices = data
|
|
if (
|
|
this.selectedRecurringInvoices.length ===
|
|
this.recurringInvoices.length
|
|
) {
|
|
this.selectAllField = true
|
|
} else {
|
|
this.selectAllField = false
|
|
}
|
|
},
|
|
|
|
selectAllRecurringInvoices() {
|
|
if (
|
|
this.selectedRecurringInvoices.length ===
|
|
this.recurringInvoices.length
|
|
) {
|
|
this.selectedRecurringInvoices = []
|
|
this.selectAllField = false
|
|
} else {
|
|
let allInvoiceIds = this.recurringInvoices.map(
|
|
(invoice) => invoice.id,
|
|
)
|
|
this.selectedRecurringInvoices = allInvoiceIds
|
|
this.selectAllField = true
|
|
}
|
|
},
|
|
|
|
addItem() {
|
|
this.newRecurringInvoice.items.push({
|
|
...recurringInvoiceItemStub,
|
|
id: Guid.raw(),
|
|
taxes: [{ ...TaxStub, id: Guid.raw() }],
|
|
})
|
|
},
|
|
|
|
removeItem(index) {
|
|
this.newRecurringInvoice.items.splice(index, 1)
|
|
},
|
|
|
|
updateItem(data) {
|
|
Object.assign(this.newRecurringInvoice.items[data.index], { ...data })
|
|
},
|
|
|
|
async fetchRecurringInvoiceInitialSettings(isEdit) {
|
|
const companyStore = useCompanyStore()
|
|
const customerStore = useCustomerStore()
|
|
const itemStore = useItemStore()
|
|
const invoiceStore = useInvoiceStore()
|
|
const taxTypeStore = useTaxTypeStore()
|
|
const route = useRoute()
|
|
const notesStore = useNotesStore()
|
|
|
|
this.isFetchingInitialSettings = true
|
|
this.newRecurringInvoice.currency = companyStore.selectedCompanyCurrency
|
|
|
|
if (route.query.customer) {
|
|
let response = await customerStore.fetchCustomer(route.query.customer)
|
|
this.newRecurringInvoice.customer = response.data.data
|
|
this.selectCustomer(response.data.data.id)
|
|
}
|
|
|
|
let editActions = []
|
|
|
|
// on create
|
|
if (!isEdit) {
|
|
await notesStore.fetchNotes()
|
|
this.newRecurringInvoice.notes =
|
|
notesStore.getDefaultNoteForType('Invoice')?.notes
|
|
this.newRecurringInvoice.tax_per_item =
|
|
companyStore.selectedCompanySettings.tax_per_item
|
|
this.newRecurringInvoice.discount_per_item =
|
|
companyStore.selectedCompanySettings.discount_per_item
|
|
this.newRecurringInvoice.sales_tax_type =
|
|
companyStore.selectedCompanySettings.sales_tax_type
|
|
this.newRecurringInvoice.sales_tax_address_type =
|
|
companyStore.selectedCompanySettings.sales_tax_address_type
|
|
this.newRecurringInvoice.starts_at = moment().format('YYYY-MM-DD')
|
|
this.newRecurringInvoice.next_invoice_date = moment()
|
|
.add(7, 'days')
|
|
.format('YYYY-MM-DD')
|
|
} else {
|
|
editActions = [this.fetchRecurringInvoice(route.params.id)]
|
|
}
|
|
|
|
Promise.all([
|
|
itemStore.fetchItems({
|
|
filter: {},
|
|
orderByField: '',
|
|
orderBy: '',
|
|
}),
|
|
this.resetSelectedNote(),
|
|
invoiceStore.fetchInvoiceTemplates(),
|
|
taxTypeStore.fetchTaxTypes({ limit: 'all' }),
|
|
...editActions,
|
|
])
|
|
.then(async ([res1, res2, res3, res4, res5]) => {
|
|
if (res3.data) {
|
|
this.templates = invoiceStore.templates
|
|
}
|
|
|
|
if (!isEdit) {
|
|
this.setTemplate(this.templates[0].name)
|
|
}
|
|
|
|
if (isEdit && res5?.data) {
|
|
let data = {
|
|
...res5.data.data,
|
|
}
|
|
|
|
this.setTemplate(res5?.data?.data?.template_name)
|
|
}
|
|
if (isEdit) {
|
|
this.addSalesTaxUs()
|
|
}
|
|
this.isFetchingInitialSettings = false
|
|
})
|
|
.catch((err) => {
|
|
console.log(err)
|
|
handleError(err)
|
|
})
|
|
},
|
|
|
|
addSalesTaxUs() {
|
|
const taxTypeStore = useTaxTypeStore()
|
|
let salesTax = { ...TaxStub }
|
|
let found = this.newRecurringInvoice.taxes.find(
|
|
(_t) => _t.name === 'Sales Tax' && _t.type === 'MODULE',
|
|
)
|
|
if (found) {
|
|
for (const key in found) {
|
|
if (Object.prototype.hasOwnProperty.call(salesTax, key)) {
|
|
salesTax[key] = found[key]
|
|
}
|
|
}
|
|
salesTax.id = found.tax_type_id
|
|
taxTypeStore.taxTypes.push(salesTax)
|
|
}
|
|
},
|
|
|
|
setTemplate(data) {
|
|
this.newRecurringInvoice.template_name = data
|
|
},
|
|
|
|
setSelectedFrequency() {
|
|
let data = this.frequencies.find((frequency) => {
|
|
return frequency.value === this.newRecurringInvoice.frequency
|
|
})
|
|
data
|
|
? (this.newRecurringInvoice.selectedFrequency = data)
|
|
: (this.newRecurringInvoice.selectedFrequency = {
|
|
label: 'Custom',
|
|
value: 'CUSTOM',
|
|
})
|
|
},
|
|
|
|
resetSelectedNote() {
|
|
this.newRecurringInvoice.selectedNote = null
|
|
},
|
|
|
|
fetchRecurringInvoiceFrequencyDate(params) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.get('/api/v1/recurring-invoice-frequency', { params })
|
|
.then((response) => {
|
|
this.newRecurringInvoice.next_invoice_at =
|
|
response.data.next_invoice_at
|
|
|
|
resolve(response)
|
|
})
|
|
.catch((err) => {
|
|
const notificationStore = useNotificationStore()
|
|
|
|
notificationStore.showNotification({
|
|
type: 'error',
|
|
message: global.t('errors.enter_valid_cron_format'),
|
|
})
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
},
|
|
})()
|
|
}
|