mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-28 19:10:40 +00:00
Edit invoice
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<span class="cell-item">
|
||||
{{ row.data.attributes.invoiceNumber }}
|
||||
{{ row.data.attributes.invoice_number }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@@ -32,7 +32,7 @@
|
||||
<a @click="downloadItem(row)">
|
||||
<DownloadCloudIcon size="15" class="icon" />
|
||||
</a>
|
||||
<router-link :to="{name: 'ClientDetail'}">
|
||||
<router-link :to="{name: 'EditInvoice', params: {id: row.data.id}}">
|
||||
<edit2-icon size="15" class="icon" />
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="{'is-offset': isDiscount && invoice.discount_rate > 0}">
|
||||
<div v-if="isVatPayer" :class="{'is-offset': isDiscount && invoice.discount_rate > 0}">
|
||||
<div v-for="(tax, i) in taxBased" :key="i" class="row small">
|
||||
<div class="cell">
|
||||
<span>{{ $t('in_editor.summary.vat_base') }} {{ tax.rate }}%</span>
|
||||
@@ -288,7 +288,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="{'is-offset': taxSummary.length > 1}">
|
||||
<div v-if="isVatPayer" :class="{'is-offset': taxSummary.length > 1}">
|
||||
<div v-for="(tax, i) in taxSummary" :key="i" class="row small">
|
||||
<div class="cell">
|
||||
<span>{{ $t('in_editor.summary.vat') }} {{ tax.rate }}%</span>
|
||||
@@ -474,7 +474,7 @@
|
||||
let total_without_tax = (item.price * item.amount)
|
||||
|
||||
// Count tax
|
||||
if (item.tax_rate) {
|
||||
if (this.isVatPayer && item.tax_rate) {
|
||||
total_without_tax += total_without_tax * (item.tax_rate / 100)
|
||||
}
|
||||
|
||||
@@ -535,8 +535,8 @@
|
||||
price: undefined,
|
||||
}
|
||||
],
|
||||
discount_type: undefined,
|
||||
discount_rate: undefined,
|
||||
discount_type: 'percent',
|
||||
discount_rate: 10,
|
||||
client: '',
|
||||
client_avatar: '',
|
||||
client_name: '',
|
||||
@@ -552,16 +552,6 @@
|
||||
send_invoice: true,
|
||||
store_client: true,
|
||||
},
|
||||
invoiceTypeList: [
|
||||
{
|
||||
label: 'Regular Invoice',
|
||||
value: 'regular-invoice',
|
||||
},
|
||||
{
|
||||
label: 'Advance Invoice',
|
||||
value: 'advance-invoice',
|
||||
},
|
||||
],
|
||||
discountTypeList: [
|
||||
{
|
||||
label: this.$t('in_editor.discount_type_percent'),
|
||||
@@ -583,15 +573,6 @@
|
||||
this.invoice.client_city = response.data.city
|
||||
this.invoice.client_postal_code = response.data.addr_zip
|
||||
this.fullDetails = response.data.name + ' ' + response.data.addr_full
|
||||
|
||||
//this.$refs.createUser.reset()
|
||||
})
|
||||
.catch(error => {
|
||||
/*if (error.response.status == 404) {
|
||||
this.$refs.createUser.setErrors({
|
||||
'ICO': 'Nič sa nenašlo :('
|
||||
});
|
||||
}*/
|
||||
})
|
||||
}, 300),
|
||||
formatCurrency(value) {
|
||||
@@ -690,7 +671,6 @@
|
||||
})
|
||||
|
||||
this.$nextTick(() => this.$refs.duplicatorItemTitle.slice(-1).pop().focus())
|
||||
|
||||
},
|
||||
removeRow(item) {
|
||||
if (this.invoice.items.length > 1)
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
<template>
|
||||
<div id="single-page">
|
||||
<MobileHeader :title="pageTitle" />
|
||||
<PageHeader :title="pageTitle" />
|
||||
|
||||
<div id="page-content">
|
||||
<div class="content-page" v-if="! isLoadingPage">
|
||||
<ValidationObserver @submit.prevent="updateInvoice" ref="updateInvoice" v-slot="{ invalid }" tag="form" class="form block-form">
|
||||
<PageTab>
|
||||
|
||||
<!--Properties-->
|
||||
<PageTabGroup>
|
||||
<FormLabel icon="tool">{{ $t('in_editor.properties') }}</FormLabel>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_number') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="invoice_number" rules="required" v-slot="{ errors }">
|
||||
<input v-model.number="invoice.invoice_number" :placeholder="$t('in_editor.plac.invoice_number')" type="number" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<small v-if="latestInvoiceNumber" class="input-help">
|
||||
{{ $t('in_number_desc', {number: latestInvoiceNumber}) }}
|
||||
</small>
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_variable') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="variable_number" rules="required" v-slot="{ errors }">
|
||||
<input v-model.number="invoice.variable_number" :placeholder="$t('in_editor.plac.variable_number')" type="number" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<small v-if="latestInvoiceNumber" class="input-help">
|
||||
{{ $t('in_variable_desc') }}
|
||||
</small>
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_delivery_at') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="delivery_at" rules="required" v-slot="{ errors }">
|
||||
<input v-model="invoice.delivery_at" type="date" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
</PageTabGroup>
|
||||
|
||||
<!--Client-->
|
||||
<PageTabGroup>
|
||||
<FormLabel icon="user">{{ $t('in_editor.client') }}</FormLabel>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_editor.client') }}:</label>
|
||||
<div class="input-wrapper">
|
||||
<input v-model.number="invoice.client['name']" type="text" disabled />
|
||||
</div>
|
||||
</div>
|
||||
</PageTabGroup>
|
||||
|
||||
<!--Items-->
|
||||
<PageTabGroup>
|
||||
<FormLabel icon="edit">{{ $t('in_editor.items') }}</FormLabel>
|
||||
|
||||
<div class="duplicator">
|
||||
<div class="plan-item duplicator-item" v-for="(item, index) in invoice.items" :key="index++">
|
||||
<x-icon @click="removeRow(item)" v-if="index !== 1" size="22" class="delete-item hover-text-theme" />
|
||||
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_editor.description') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="description" rules="required" v-slot="{ errors }">
|
||||
<input v-model="item.description" ref="duplicatorItemTitle" :placeholder="$t('in_editor.plac.item_desc')" type="text" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-inline">
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_editor.amount') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="amount" rules="required" v-slot="{ errors }">
|
||||
<input v-model.number="item.amount" :placeholder="$t('in_editor.plac.item_amount')" type="number" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<div v-if="isVatPayer" class="block-wrapper">
|
||||
<label>{{ $t('in_editor.tax_rate') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="tax_rate" rules="required" v-slot="{ errors }">
|
||||
<input v-model.number="item.tax_rate" :placeholder="$t('in_editor.plac.item_tax_rate')" type="number" step="1" min="1" max="100" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<label>{{ $t('in_editor.price') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="price" rules="required" v-slot="{ errors }">
|
||||
<input v-model.number="item.price" :placeholder="$t('in_editor.plac.item_price')" type="text" pattern="[0-9]{1,4}(\.[0-9]{2})?" step="0.01" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ButtonBase @click.native="addRow" class="duplicator-add-button" button-style="theme">
|
||||
{{ $t('in_editor.add_item') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</PageTabGroup>
|
||||
|
||||
<!--Discount-->
|
||||
<PageTabGroup>
|
||||
<FormLabel icon="credit-card">
|
||||
{{ $t('in_editor.discount') }}
|
||||
</FormLabel>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<div class="input-wrapper">
|
||||
<div class="inline-wrapper">
|
||||
<div class="switch-label">
|
||||
<label class="input-label">{{ $t('in_editor.apply_discount') }}:</label>
|
||||
<small class="input-help">{{ $t('in_editor.discount_help') }}</small>
|
||||
</div>
|
||||
<SwitchInput v-model="isDiscount" class="switch" :state="isDiscount" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isDiscount" class="block-wrapper">
|
||||
<label>{{ $t('in_editor.discount_type') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="discount_type" rules="required" v-slot="{ errors }">
|
||||
<SelectInput v-model="invoice.discount_type" :default="invoice.discount_type" :options="discountTypeList" :placeholder="$t('in_editor.plac.discount_type')" :isError="errors[0]" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<div v-if="isDiscount" class="block-wrapper">
|
||||
<label>{{ $t('in_editor.discount_rate') }}:</label>
|
||||
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="discount_rate" rules="required" v-slot="{ errors }">
|
||||
<input v-model.number="invoice.discount_rate" :placeholder="$t('in_editor.plac.discount_rate')" max="100" min="0" type="number" :class="{'is-error': errors[0]}" class="focus-border-theme" />
|
||||
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
</PageTabGroup>
|
||||
|
||||
<!--Others-->
|
||||
<PageTabGroup>
|
||||
<FormLabel icon="settings">{{ $t('in_editor.others') }}</FormLabel>
|
||||
|
||||
<div v-if="invoice.client_email" class="block-wrapper">
|
||||
<div class="input-wrapper">
|
||||
<div class="inline-wrapper">
|
||||
<div class="switch-label">
|
||||
<label class="input-label">{{ $t('in_editor.store_client') }}:</label>
|
||||
<small class="input-help">{{ $t('in_editor.store_client_notes') }}</small>
|
||||
</div>
|
||||
<SwitchInput v-model="invoice.send_invoice" class="switch" :state="invoice.send_invoice" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-wrapper">
|
||||
<div class="input-wrapper">
|
||||
<div class="inline-wrapper">
|
||||
<div class="switch-label">
|
||||
<label class="input-label">{{ $t('in_editor.send') }}:</label>
|
||||
<small class="input-help">{{ $t('in_editor.send_notes') }}</small>
|
||||
</div>
|
||||
<SwitchInput v-model="invoice.send_invoice" class="switch" :state="invoice.send_invoice" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageTabGroup>
|
||||
</PageTab>
|
||||
</ValidationObserver>
|
||||
<div class="summary">
|
||||
<FormLabel icon="credit-card">
|
||||
{{ $t('in_editor.summary') }}
|
||||
</FormLabel>
|
||||
<div class="summary-list" :class="{'is-error': isError}">
|
||||
|
||||
<div v-if="isDiscount && invoice.discount_rate > 0" class="row small">
|
||||
<div class="cell">
|
||||
<span>{{ $t('in_editor.discount') }}</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<span>-{{ invoice.discount_type === 'percent' ? formatNumber(invoice.discount_rate) + '%' : formatCurrency(invoice.discount_rate) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isVatPayer" :class="{'is-offset': isDiscount && invoice.discount_rate > 0}">
|
||||
<div v-for="(tax, i) in taxBased" :key="i" class="row small">
|
||||
<div class="cell">
|
||||
<span>{{ $t('in_editor.summary.vat_base') }} {{ tax.rate }}%</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<span>{{ formatCurrency(tax.total) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isVatPayer" :class="{'is-offset': taxSummary.length > 1}">
|
||||
<div v-for="(tax, i) in taxSummary" :key="i" class="row small">
|
||||
<div class="cell">
|
||||
<span>{{ $t('in_editor.summary.vat') }} {{ tax.rate }}%</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<span>{{ formatCurrency(tax.total) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" :class="{'row-summary': total > 0}">
|
||||
<div class="cell">
|
||||
<b>{{ $t('in_editor.summary.total') }}</b>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<b>{{ formatCurrency(total) }}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ButtonBase :disabled="isLoading" @click.native="deleteInvoice" button-style="danger" class="next-submit">
|
||||
Delete Invoice
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase :disabled="isLoading" :loading="isLoading" @click.native="updateInvoice" button-style="theme-solid" class="next-submit" style="margin-top: 10px">
|
||||
{{ $t('in_editor.submit') }}
|
||||
</ButtonBase>
|
||||
<p class="error-message" v-if="isError">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loader" v-if="isLoadingPage">
|
||||
<Spinner />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
|
||||
import SwitchInput from '@/components/Others/Forms/SwitchInput'
|
||||
import SelectInput from '@/components/Others/Forms/SelectInput'
|
||||
import ImageInput from '@/components/Others/Forms/ImageInput'
|
||||
import FormLabel from '@/components/Others/Forms/FormLabel'
|
||||
import MobileHeader from '@/components/Mobile/MobileHeader'
|
||||
import ButtonBase from '@/components/FilesView/ButtonBase'
|
||||
import PageTab from '@/components/Others/Layout/PageTab'
|
||||
import PageHeader from '@/components/Others/PageHeader'
|
||||
import InfoBox from '@/components/Others/Forms/InfoBox'
|
||||
import Spinner from '@/components/FilesView/Spinner'
|
||||
import {required} from 'vee-validate/dist/rules'
|
||||
import {XIcon} from 'vue-feather-icons'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from "@/bus"
|
||||
import axios from "axios"
|
||||
import {debounce} from "lodash"
|
||||
|
||||
export default {
|
||||
name: 'CreateInvoice',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
PageTabGroup,
|
||||
MobileHeader,
|
||||
SelectInput,
|
||||
SwitchInput,
|
||||
ImageInput,
|
||||
PageHeader,
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
required,
|
||||
Spinner,
|
||||
InfoBox,
|
||||
PageTab,
|
||||
XIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'countries',
|
||||
'config',
|
||||
]),
|
||||
pageTitle() {
|
||||
return {
|
||||
'regular-invoice': this.$t('in_editor.page.edit_regular_invoice'),
|
||||
'advance-invoice': this.$t('in_editor.page.edit_advance_invoice'),
|
||||
}[this.invoice.invoice_type]
|
||||
},
|
||||
taxBased() {
|
||||
let bag = [];
|
||||
|
||||
this.invoice.items.forEach(item => {
|
||||
|
||||
if (item.price && item.amount && item.tax_rate) {
|
||||
|
||||
if (!bag.find(bagItem => bagItem.rate === item.tax_rate)) {
|
||||
|
||||
bag.push({
|
||||
rate: item.tax_rate,
|
||||
total: (item.price * item.amount),
|
||||
})
|
||||
} else {
|
||||
bag.find(bagItem => {
|
||||
|
||||
// Count total tax rate for percentage
|
||||
if (bagItem.rate === item.tax_rate) {
|
||||
bagItem.total += (item.price * item.amount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Count discount
|
||||
if (this.isDiscount) {
|
||||
bag.forEach(bagItem => {
|
||||
|
||||
if (this.invoice.discount_type === 'percent') {
|
||||
bagItem.total -= bagItem.total * (this.invoice.discount_rate / 100)
|
||||
}
|
||||
|
||||
if (this.invoice.discount_type === 'value') {
|
||||
let percentage_of_discount = this.invoice.discount_rate / (this.total + this.invoice.discount_rate)
|
||||
|
||||
bagItem.total -= bagItem.total * percentage_of_discount
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return bag
|
||||
},
|
||||
taxSummary() {
|
||||
let bag = [];
|
||||
|
||||
this.invoice.items.forEach(item => {
|
||||
|
||||
if (item.price && item.amount && item.tax_rate) {
|
||||
|
||||
if (!bag.find(bagItem => bagItem.rate === item.tax_rate)) {
|
||||
|
||||
bag.push({
|
||||
rate: item.tax_rate,
|
||||
total: (item.price * item.amount) * (item.tax_rate / 100),
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
bag.map(bagItem => {
|
||||
|
||||
// Count total tax rate for percentage
|
||||
if (bagItem.rate === item.tax_rate) {
|
||||
bagItem.total += (item.price * item.amount) * (item.tax_rate / 100)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Count discount
|
||||
if (this.isDiscount) {
|
||||
bag.forEach(bagItem => {
|
||||
|
||||
if (this.invoice.discount_type === 'percent') {
|
||||
bagItem.total -= bagItem.total * (this.invoice.discount_rate / 100)
|
||||
}
|
||||
|
||||
if (this.invoice.discount_type === 'value') {
|
||||
let percentage_of_discount = this.invoice.discount_rate / (this.total + this.invoice.discount_rate)
|
||||
|
||||
bagItem.total -= bagItem.total * percentage_of_discount
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return bag
|
||||
},
|
||||
total() {
|
||||
let total = 0;
|
||||
|
||||
this.invoice.items.forEach(item => {
|
||||
if (item.price && item.amount) {
|
||||
|
||||
let total_without_tax = (item.price * item.amount)
|
||||
|
||||
// Count tax
|
||||
if (this.isVatPayer && item.tax_rate) {
|
||||
total_without_tax += total_without_tax * (item.tax_rate / 100)
|
||||
}
|
||||
|
||||
total += total_without_tax
|
||||
}
|
||||
})
|
||||
|
||||
// Count discount
|
||||
if (this.isDiscount) {
|
||||
|
||||
if (this.invoice.discount_type === 'percent') {
|
||||
total -= total * (this.invoice.discount_rate / 100)
|
||||
}
|
||||
|
||||
if (this.invoice.discount_type === 'value') {
|
||||
total -= this.invoice.discount_rate
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isDiscount(val) {
|
||||
if (!val) {
|
||||
this.invoice.discount_rate = null
|
||||
this.invoice.discount_type = null
|
||||
}
|
||||
},
|
||||
'invoice.invoice_number': function (val) {
|
||||
this.invoice.variable_number = val
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fullDetails: undefined,
|
||||
isLoadingPage: true,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
isDiscount: false,
|
||||
isVatPayer: false,
|
||||
clients: [],
|
||||
latestInvoiceNumber: undefined,
|
||||
invoice: {
|
||||
invoice_type: '',
|
||||
invoice_number: '',
|
||||
variable_number: '',
|
||||
delivery_at: '',
|
||||
items: [
|
||||
{
|
||||
id: Math.floor(Math.random() * 10000000),
|
||||
description: '',
|
||||
amount: 1,
|
||||
tax_rate: 21,
|
||||
price: undefined,
|
||||
}
|
||||
],
|
||||
discount_type: undefined,
|
||||
discount_rate: undefined,
|
||||
client: '',
|
||||
send_invoice: false,
|
||||
},
|
||||
discountTypeList: [
|
||||
{
|
||||
label: this.$t('in_editor.discount_type_percent'),
|
||||
value: 'percent',
|
||||
},
|
||||
{
|
||||
label: this.$t('in_editor.discount_type_amount'),
|
||||
value: 'value',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatCurrency(value) {
|
||||
return new Intl
|
||||
.NumberFormat('cs-CS', {
|
||||
style: 'currency',
|
||||
currency: 'CZK'
|
||||
})
|
||||
.format(value)
|
||||
},
|
||||
formatNumber(value) {
|
||||
return (Math.round(value * 100) / 100)
|
||||
.toFixed(2);
|
||||
},
|
||||
deleteInvoice() {
|
||||
events.$emit('confirm:open', {
|
||||
title: `Are you sure you want to delete this invoice?`,
|
||||
message: 'Your invoice will be permanently deleted.',
|
||||
buttonColor: 'danger-solid',
|
||||
action: {
|
||||
id: this.$route.params.id,
|
||||
operation: 'delete-invoice'
|
||||
}
|
||||
})
|
||||
},
|
||||
async updateInvoice() {
|
||||
const isValid = await this.$refs.updateInvoice.validate();
|
||||
|
||||
if (!isValid) {
|
||||
this.isError = true
|
||||
this.errorMessage = this.$t('in_editor.error')
|
||||
return
|
||||
}
|
||||
|
||||
// Start loading
|
||||
this.isLoading = true
|
||||
|
||||
// Create form
|
||||
let formData = new FormData()
|
||||
|
||||
// Append data to form
|
||||
Object.keys(this.invoice).forEach(key => {
|
||||
|
||||
if (key === 'items') {
|
||||
formData.append(key, JSON.stringify(this.invoice[key]))
|
||||
} else {
|
||||
if (this.invoice[key])
|
||||
formData.append(key, this.invoice[key])
|
||||
}
|
||||
})
|
||||
|
||||
// Send request to get user token
|
||||
axios
|
||||
.post(`/api/oasis/invoices/${this.$route.params.id}`, formData)
|
||||
.then(() => {
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: 'The invoice was successfully edited.',
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
this.isError = true
|
||||
|
||||
if (error.response.status === 422) {
|
||||
|
||||
Object.keys(error.response.data.errors).forEach(key => {
|
||||
|
||||
let obj = {};
|
||||
obj[key] = error.response.data.errors[key]
|
||||
|
||||
this.$refs.updateInvoice.setErrors(obj);
|
||||
})
|
||||
|
||||
} else {
|
||||
events.$emit('alert:open', {
|
||||
title: this.$t('popup_error.title'),
|
||||
message: this.$t('popup_error.message'),
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
addRow() {
|
||||
let lastTaxRate = this.invoice.items.slice(-1).pop()
|
||||
|
||||
this.invoice.items.push({
|
||||
id: Math.floor(Math.random() * 10000000),
|
||||
description: '',
|
||||
amount: 1,
|
||||
tax_rate: lastTaxRate?.tax_rate || 21,
|
||||
price: 1,
|
||||
})
|
||||
|
||||
this.$nextTick(() => this.$refs.duplicatorItemTitle.slice(-1).pop().focus())
|
||||
},
|
||||
removeRow(item) {
|
||||
if (this.invoice.items.length > 1)
|
||||
this.invoice.items = this.invoice.items.filter(obj => obj.id !== item.id)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
axios.get('/api/oasis/invoices/editor')
|
||||
.then(response => {
|
||||
this.isVatPayer = response.data.isVatPayer
|
||||
})
|
||||
|
||||
axios.get(`/api/oasis/invoices/${this.$route.params.id}`)
|
||||
.then(response => {
|
||||
this.invoice.invoice_number = response.data.data.attributes.invoice_number
|
||||
this.invoice.variable_number = response.data.data.attributes.variable_number
|
||||
this.invoice.invoice_type = response.data.data.attributes.invoice_type
|
||||
this.invoice.delivery_at = response.data.data.attributes.delivery_at
|
||||
this.invoice.items = response.data.data.attributes.items
|
||||
this.invoice.discount_type = response.data.data.attributes.discount_type
|
||||
this.invoice.discount_rate = response.data.data.attributes.discount_rate
|
||||
this.invoice.client = response.data.data.attributes.client
|
||||
|
||||
if (this.invoice.discount_type && this.invoice.discount_rate) {
|
||||
this.isDiscount = true
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoadingPage = false
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@assets/vuefilemanager/_variables';
|
||||
@import '@assets/vuefilemanager/_mixins';
|
||||
@import '@assets/vuefilemanager/_forms';
|
||||
|
||||
#page-content {
|
||||
max-width: 1100px;
|
||||
}
|
||||
|
||||
.content-page {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
margin-bottom: 30px;
|
||||
gap: 50px;
|
||||
|
||||
.form {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-list {
|
||||
box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.06);
|
||||
border-radius: 8px;
|
||||
min-width: 300px;
|
||||
position: sticky;
|
||||
padding: 25px;
|
||||
top: 85px;
|
||||
|
||||
&.is-error {
|
||||
border: 2px solid $danger;
|
||||
box-shadow: 0 7px 20px 5px rgba($danger, 0.06);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.next-submit {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
@include font-size(12);
|
||||
line-height: 1.6;
|
||||
display: block;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.is-offset {
|
||||
border-top: 1px solid $light_mode_border;
|
||||
display: block;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 15px 0;
|
||||
|
||||
&.small {
|
||||
padding: 0 0 10px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.row-summary {
|
||||
border-top: 1px solid $light_mode_border;
|
||||
padding-bottom: 0;
|
||||
|
||||
b {
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
b {
|
||||
display: block;
|
||||
@include font-size(18);
|
||||
}
|
||||
|
||||
small {
|
||||
color: $text-muted;
|
||||
@include font-size(12);
|
||||
}
|
||||
|
||||
span {
|
||||
@include font-size(14);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.duplicator {
|
||||
|
||||
.wrapper-inline {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 970px) {
|
||||
.content-page {
|
||||
grid-template-columns: 1fr;
|
||||
margin-bottom: 30px;
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
<!--Invoice-->
|
||||
<div v-show="isInvoice" class="menu-options" id="menu-list">
|
||||
<OptionGroup class="menu-option-group">
|
||||
<Option @click.native="" title="Edit Invoice" icon="rename" />
|
||||
<Option @click.native="editItem" title="Edit Invoice" icon="rename" />
|
||||
<Option @click.native="" title="Send Invoice" icon="send" />
|
||||
<Option @click.native="goToCompany" title="Go to Company" icon="user" />
|
||||
<Option @click.native="deleteItem" :title="$t('context_menu.delete')" icon="trash" />
|
||||
@@ -95,9 +95,12 @@ export default {
|
||||
// Show panel if is not open
|
||||
this.$store.dispatch('fileInfoToggle', true)
|
||||
},
|
||||
editItem() {
|
||||
this.$router.push({name: 'EditInvoice', params: {id: this.item.id}})
|
||||
},
|
||||
deleteItem() {
|
||||
events.$emit('confirm:open', {
|
||||
title: `Are you sure you want to delete invoice number ${this.item.invoiceNumber}?`,
|
||||
title: `Are you sure you want to delete invoice number ${this.item.invoice_number}?`,
|
||||
message: 'Your invoice will be permanently deleted.',
|
||||
buttonColor: 'danger-solid',
|
||||
action: {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<!--Invoice Controls-->
|
||||
<ToolbarGroup v-if="! $isMobile()">
|
||||
<ToolbarButton @click.native="shareInvoice" :class="{'is-inactive': canActiveInView }" source="send" :action="$t('actions.share')" />
|
||||
<ToolbarButton @click.native="shareInvoice" :class="{'is-inactive': canActiveInView }" source="rename" :action="$t('actions.share')" />
|
||||
<ToolbarButton @click.native="editInvoice" :class="{'is-inactive': canActiveInView }" source="rename" :action="$t('actions.share')" />
|
||||
<ToolbarButton @click.native="deleteInvoice" :class="{'is-inactive': canActiveInView }" source="trash" :action="$t('actions.delete')" />
|
||||
</ToolbarGroup>
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
if (this.clipboard.length > 0) {
|
||||
|
||||
events.$emit('confirm:open', {
|
||||
title: `Are you sure you want to delete invoice number ${this.clipboard[0].invoiceNumber}?`,
|
||||
title: `Are you sure you want to delete invoice number ${this.clipboard[0].invoice_number}?`,
|
||||
message: 'Your invoice will be permanently deleted.',
|
||||
buttonColor: 'danger-solid',
|
||||
action: {
|
||||
@@ -144,6 +144,9 @@
|
||||
shareInvoice() {
|
||||
alert('Send Invoice')
|
||||
},
|
||||
editInvoice() {
|
||||
this.$router.push({name: 'EditInvoice', params: {id: this.clipboard[0].id}})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
<div v-if="isSingleFile && !isEmpty" class="info-headline">
|
||||
<TitlePreview
|
||||
icon="file-text"
|
||||
:title="singleFile.clientName"
|
||||
:subtitle="'Invoice - ' + singleFile.invoiceNumber"
|
||||
:title="singleFile.client_name"
|
||||
:subtitle="'Invoice - ' + singleFile.invoice_number"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
<ListInfoItem
|
||||
title="Invoice Number"
|
||||
:content="singleFile.invoiceNumber"
|
||||
:content="singleFile.invoice_number"
|
||||
/>
|
||||
|
||||
<ListInfoItem
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
<ListInfoItem
|
||||
title="Client"
|
||||
:content="singleFile.clientName"
|
||||
:content="singleFile.client_name"
|
||||
/>
|
||||
|
||||
<!--Created At-->
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
<div class="item-name">
|
||||
|
||||
<b :ref="item.id" class="name">
|
||||
{{ item.clientName }} - {{ item.total }}
|
||||
{{ item.client_name }} - {{ item.total }}
|
||||
</b>
|
||||
|
||||
<div class="item-info">
|
||||
<span class="item-size">
|
||||
{{ item.created_at }}, n. {{ item.invoiceNumber }}
|
||||
{{ item.created_at }}, n. {{ item.invoice_number }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class="headline"
|
||||
icon="file-text"
|
||||
:title="clipboard[0].name"
|
||||
:subtitle="'Invoice - ' + clipboard[0].invoiceNumber"
|
||||
:subtitle="'Invoice - ' + clipboard[0].invoice_number"
|
||||
/>
|
||||
|
||||
<!--Trash location-->
|
||||
@@ -64,7 +64,7 @@ export default {
|
||||
},
|
||||
deleteInvoice() {
|
||||
events.$emit('confirm:open', {
|
||||
title: `Are you sure you want to delete invoice number ${this.clipboard[0].invoiceNumber}?`,
|
||||
title: `Are you sure you want to delete invoice number ${this.clipboard[0].invoice_number}?`,
|
||||
message: 'Your invoice will be permanently deleted.',
|
||||
buttonColor: 'danger-solid',
|
||||
action: {
|
||||
|
||||
Reference in New Issue
Block a user