forked from verdnatura/salix-front
Reviewed-on: verdnatura/salix-front#166 Reviewed-by: Jorge Penades <jorgep@verdnatura.es>
This commit is contained in:
commit
f715c615d2
|
@ -7,6 +7,13 @@ import FetchData from 'components/FetchData.vue';
|
|||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import FormModelPopup from './FormModelPopup.vue';
|
||||
|
||||
const props = defineProps({
|
||||
showEntityField: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onDataSaved']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -73,7 +80,7 @@ const onDataSaved = (data) => {
|
|||
:rules="validate('bankEntity.countryFk')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div v-if="showEntityField" class="col">
|
||||
<QInput :label="t('id')" v-model="data.id" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
|
|
@ -19,8 +19,8 @@ const cityFormData = reactive({
|
|||
|
||||
const provincesOptions = ref([]);
|
||||
|
||||
const onDataSaved = () => {
|
||||
emit('onDataSaved');
|
||||
const onDataSaved = (dataSaved) => {
|
||||
emit('onDataSaved', dataSaved);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -28,16 +28,24 @@ const countriesOptions = ref([]);
|
|||
const provincesOptions = ref([]);
|
||||
const townsLocationOptions = ref([]);
|
||||
|
||||
const onDataSaved = () => {
|
||||
emit('onDataSaved');
|
||||
const onDataSaved = (dataSaved) => {
|
||||
emit('onDataSaved', dataSaved);
|
||||
};
|
||||
|
||||
const onCityCreated = async () => {
|
||||
const onCityCreated = async ({ name, provinceFk }, formData) => {
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.townFk = townsLocationOptions.value.find((town) => town.name === name).id;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.countryFk = provincesOptions.value.find(
|
||||
(province) => province.id === provinceFk
|
||||
).countryFk;
|
||||
};
|
||||
|
||||
const onProvinceCreated = async () => {
|
||||
const onProvinceCreated = async ({ name }, formData) => {
|
||||
await provincesFetchDataRef.value.fetch();
|
||||
formData.provinceFk = provincesOptions.value.find(
|
||||
(province) => province.name === name
|
||||
).id;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -88,7 +96,9 @@ const onProvinceCreated = async () => {
|
|||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
>
|
||||
<template #form>
|
||||
<CreateNewCityForm @on-data-saved="onCityCreated($event)" />
|
||||
<CreateNewCityForm
|
||||
@on-data-saved="onCityCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
|
@ -107,7 +117,7 @@ const onProvinceCreated = async () => {
|
|||
>
|
||||
<template #form>
|
||||
<CreateNewProvinceForm
|
||||
@on-data-saved="onProvinceCreated($event)"
|
||||
@on-data-saved="onProvinceCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
|
@ -131,7 +141,7 @@ const onProvinceCreated = async () => {
|
|||
es:
|
||||
New postcode: Nuevo código postal
|
||||
Please, ensure you put the correct data!: ¡Por favor, asegúrese de poner los datos correctos!
|
||||
City: Ciudad
|
||||
City: Población
|
||||
Province: Provincia
|
||||
Country: País
|
||||
Postcode: Código postal
|
||||
|
|
|
@ -19,8 +19,8 @@ const provinceFormData = reactive({
|
|||
|
||||
const autonomiesOptions = ref([]);
|
||||
|
||||
const onDataSaved = () => {
|
||||
emit('onDataSaved');
|
||||
const onDataSaved = (dataSaved) => {
|
||||
emit('onDataSaved', dataSaved);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -225,15 +225,19 @@ function getDifferences(obj1, obj2) {
|
|||
delete obj2.$index;
|
||||
|
||||
for (let key in obj1) {
|
||||
if (obj2[key] && obj1[key] !== obj2[key]) {
|
||||
if (obj2[key] && JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
|
||||
diff[key] = obj2[key];
|
||||
}
|
||||
}
|
||||
for (let key in obj2) {
|
||||
if (obj1[key] === undefined || obj1[key] !== obj2[key]) {
|
||||
if (
|
||||
obj1[key] === undefined ||
|
||||
JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])
|
||||
) {
|
||||
diff[key] = obj2[key];
|
||||
}
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,13 +128,14 @@ async function save() {
|
|||
|
||||
try {
|
||||
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||
let response
|
||||
if ($props.urlCreate) {
|
||||
await axios.post($props.urlCreate, body);
|
||||
response = await axios.post($props.urlCreate, body);
|
||||
notify('globals.dataCreated', 'positive');
|
||||
} else {
|
||||
await axios.patch($props.urlUpdate || $props.url, body);
|
||||
response = await axios.patch($props.urlUpdate || $props.url, body);
|
||||
}
|
||||
emit('onDataSaved', formData.value);
|
||||
emit('onDataSaved', formData.value, response);
|
||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||
hasChanges.value = false;
|
||||
} catch (err) {
|
||||
|
|
|
@ -42,8 +42,8 @@ const { t } = useI18n();
|
|||
const closeButton = ref(null);
|
||||
const isLoading = ref(false);
|
||||
|
||||
const onDataSaved = () => {
|
||||
emit('onDataSaved');
|
||||
const onDataSaved = (dataSaved) => {
|
||||
emit('onDataSaved', dataSaved);
|
||||
closeForm();
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ const closeForm = () => {
|
|||
:default-actions="false"
|
||||
:url-create="urlCreate"
|
||||
:model="model"
|
||||
@on-data-saved="onDataSaved()"
|
||||
@on-data-saved="onDataSaved($event)"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'update:options']);
|
||||
const emit = defineEmits(['update:modelValue', 'update:options', 'keyup.enter']);
|
||||
|
||||
const $props = defineProps({
|
||||
modelValue: {
|
||||
|
@ -32,6 +32,10 @@ const styleAttrs = computed(() => {
|
|||
}
|
||||
: {};
|
||||
});
|
||||
|
||||
const onEnterPress = () => {
|
||||
emit('keyup.enter');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -41,6 +45,7 @@ const styleAttrs = computed(() => {
|
|||
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||
type="text"
|
||||
:class="{ required: $attrs.required }"
|
||||
@keyup.enter="onEnterPress()"
|
||||
>
|
||||
<template v-if="$slots.prepend" #prepend>
|
||||
<slot name="prepend" />
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
<script setup>
|
||||
import {computed, ref} from 'vue';
|
||||
import { toHour} from 'src/filters';
|
||||
import {useI18n} from "vue-i18n";
|
||||
import isValidDate from "filters/isValidDate";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isOutlined: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const value = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
const [hours, minutes] = value.split(':')
|
||||
const date = new Date()
|
||||
date.setUTCHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0)
|
||||
emit('update:modelValue', value ? date.toISOString() : null);
|
||||
},
|
||||
});
|
||||
|
||||
const onDateUpdate = (date) => {
|
||||
internalValue.value = date;
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
value.value = internalValue.value;
|
||||
};
|
||||
const formatTime = (dateString) => {
|
||||
if (!isValidDate(dateString)){
|
||||
return ''
|
||||
}
|
||||
const date = new Date(dateString || '');
|
||||
return `${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const internalValue = ref(formatTime(value))
|
||||
|
||||
const styleAttrs = computed(() => {
|
||||
return props.isOutlined
|
||||
? {
|
||||
dense: true,
|
||||
outlined: true,
|
||||
rounded: true,
|
||||
}
|
||||
: {};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QInput
|
||||
class="vn-input-time"
|
||||
rounded
|
||||
readonly
|
||||
:model-value="toHour(value)"
|
||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
:no-parent-event="props.readonly"
|
||||
>
|
||||
<QTime
|
||||
:format24h="false"
|
||||
:model-value="formatTime(value)"
|
||||
@update:model-value="onDateUpdate"
|
||||
>
|
||||
<div class="row items-center justify-end q-gutter-sm">
|
||||
<QBtn :label="t('Cancel')" color="primary" flat v-close-popup />
|
||||
<QBtn label="Ok" color="primary" flat @click="save" v-close-popup />
|
||||
</div>
|
||||
</QTime>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.vn-input-time.q-field--standard.q-field--readonly .q-field__control:before {
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.vn-input-time.q-field--outlined.q-field--readonly .q-field__control:before {
|
||||
border-style: solid;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Cancel: Cancelar
|
||||
</i18n>
|
|
@ -9,6 +9,7 @@ const $props = defineProps({
|
|||
isSelected: { type: Boolean, default: false },
|
||||
title: { type: String, default: null },
|
||||
showCheckbox: { type: Boolean, default: false },
|
||||
hasInfoIcons: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['toggleCardCheck']);
|
||||
|
@ -39,6 +40,9 @@ const toggleCardCheck = (item) => {
|
|||
</div>
|
||||
</slot>
|
||||
<div class="card-list-body">
|
||||
<div v-if="hasInfoIcons" class="column q-mr-md q-gutter-y-xs">
|
||||
<slot name="info-icons" />
|
||||
</div>
|
||||
<div class="list-items row flex-wrap-wrap">
|
||||
<slot name="list-items" />
|
||||
</div>
|
||||
|
|
|
@ -17,9 +17,9 @@ a {
|
|||
// Removes chrome autofill background
|
||||
input:-webkit-autofill,
|
||||
select:-webkit-autofill {
|
||||
color: var(--vn-text) ;
|
||||
color: var(--vn-text);
|
||||
font-family: $typography-font-family;
|
||||
-webkit-text-fill-color: var(--vn-text) ;
|
||||
-webkit-text-fill-color: var(--vn-text);
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
}
|
||||
|
@ -48,6 +48,10 @@ body.body--dark {
|
|||
background-color: var(--vn-dark);
|
||||
}
|
||||
|
||||
.color-vn-text {
|
||||
color: var(--vn-text);
|
||||
}
|
||||
|
||||
.vn-card {
|
||||
background-color: var(--vn-gray);
|
||||
color: var(--vn-text);
|
||||
|
|
|
@ -8,11 +8,13 @@ import toPercentage from './toPercentage';
|
|||
import toLowerCamel from './toLowerCamel';
|
||||
import dashIfEmpty from './dashIfEmpty';
|
||||
import dateRange from './dateRange';
|
||||
import toHour from './toHour';
|
||||
|
||||
export {
|
||||
toLowerCase,
|
||||
toLowerCamel,
|
||||
toDate,
|
||||
toHour,
|
||||
toDateString,
|
||||
toDateHour,
|
||||
toRelativeDate,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export default function isValidDate(date) {
|
||||
return !isNaN(new Date(date).getTime());
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import isValidDate from 'filters/isValidDate';
|
||||
|
||||
export default function toHour(date) {
|
||||
if (!isValidDate(date)) {
|
||||
return '--:--';
|
||||
}
|
||||
const dateHour = new Date(date);
|
||||
let hours = dateHour.getUTCHours();
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12;
|
||||
|
||||
let minutes = dateHour.getUTCMinutes();
|
||||
minutes = minutes < 10 ? minutes.toString().padStart(2, '0') : minutes;
|
||||
|
||||
return `${hours}:${minutes} ${dateHour.getUTCHours() >= 12 ? 'PM' : 'AM'}`;
|
||||
}
|
|
@ -261,12 +261,89 @@ export default {
|
|||
pageTitles: {
|
||||
entries: 'Entries',
|
||||
list: 'List',
|
||||
createEntry: 'New entry',
|
||||
summary: 'Summary',
|
||||
create: 'Create',
|
||||
basicData: 'Basic data',
|
||||
buys: 'Buys',
|
||||
notes: 'Notes',
|
||||
log: 'Log',
|
||||
},
|
||||
list: {
|
||||
newEntry: 'New entry',
|
||||
landed: 'Landed',
|
||||
invoiceNumber: 'Invoice number',
|
||||
supplier: 'Supplier',
|
||||
booked: 'Booked',
|
||||
confirmed: 'Confirmed',
|
||||
ordered: 'Ordered',
|
||||
},
|
||||
summary: {
|
||||
commission: 'Commission',
|
||||
currency: 'Currency',
|
||||
company: 'Company',
|
||||
reference: 'Reference',
|
||||
invoiceNumber: 'Invoice number',
|
||||
ordered: 'Ordered',
|
||||
confirmed: 'Confirmed',
|
||||
booked: 'Booked',
|
||||
raid: 'Raid',
|
||||
excludedFromAvailable: 'Inventory',
|
||||
travelReference: 'Reference',
|
||||
travelAgency: 'Agency',
|
||||
travelShipped: 'Shipped',
|
||||
travelWarehouseOut: 'Warehouse Out',
|
||||
travelDelivered: 'Delivered',
|
||||
travelLanded: 'Landed',
|
||||
travelWarehouseIn: 'Warehouse In',
|
||||
travelReceived: 'Received',
|
||||
buys: 'Buys',
|
||||
quantity: 'Quantity',
|
||||
stickers: 'Stickers',
|
||||
package: 'Package',
|
||||
weight: 'Weight',
|
||||
packing: 'Packing',
|
||||
grouping: 'Grouping',
|
||||
buyingValue: 'Buying value',
|
||||
import: 'Import',
|
||||
pvp: 'PVP',
|
||||
item: 'Item',
|
||||
},
|
||||
basicData: {
|
||||
supplier: 'Supplier',
|
||||
travel: 'Travel',
|
||||
reference: 'Reference',
|
||||
invoiceNumber: 'Invoice number',
|
||||
company: 'Company',
|
||||
currency: 'Currency',
|
||||
commission: 'Commission',
|
||||
observation: 'Observation',
|
||||
ordered: 'Ordered',
|
||||
confirmed: 'Confirmed',
|
||||
booked: 'Booked',
|
||||
raid: 'Raid',
|
||||
excludedFromAvailable: 'Inventory',
|
||||
},
|
||||
buys: {
|
||||
groupingPrice: 'Grouping price',
|
||||
packingPrice: 'Packing price',
|
||||
reference: 'Reference',
|
||||
observations: 'Observations',
|
||||
item: 'Item',
|
||||
description: 'Description',
|
||||
size: 'Size',
|
||||
packing: 'Packing',
|
||||
grouping: 'Grouping',
|
||||
buyingValue: 'Buying value',
|
||||
packagingFk: 'Box',
|
||||
file: 'File',
|
||||
},
|
||||
notes: {
|
||||
observationType: 'Observation type',
|
||||
description: 'Description',
|
||||
},
|
||||
descriptor: {
|
||||
agency: 'Agency',
|
||||
landed: 'Landed',
|
||||
warehouseOut: 'Warehouse Out',
|
||||
},
|
||||
},
|
||||
ticket: {
|
||||
|
@ -807,6 +884,10 @@ export default {
|
|||
pageTitles: {
|
||||
routes: 'Routes',
|
||||
cmrsList: 'External CMRs list',
|
||||
RouteList: 'List',
|
||||
create: 'Create',
|
||||
basicData: 'Basic Data',
|
||||
summary: 'Summary',
|
||||
},
|
||||
cmr: {
|
||||
list: {
|
||||
|
@ -877,6 +958,72 @@ export default {
|
|||
create: {
|
||||
supplierName: 'Supplier name',
|
||||
},
|
||||
basicData: {
|
||||
alias: 'Alias',
|
||||
workerFk: 'Responsible',
|
||||
isSerious: 'Verified',
|
||||
isActive: 'Active',
|
||||
isPayMethodChecked: 'PayMethod checked',
|
||||
note: 'Notes',
|
||||
},
|
||||
fiscalData: {
|
||||
name: 'Social name *',
|
||||
nif: 'Tax number *',
|
||||
account: 'Account',
|
||||
sageTaxTypeFk: 'Sage tax type',
|
||||
sageWithholdingFk: 'Sage withholding',
|
||||
sageTransactionTypeFk: 'Sage transaction type',
|
||||
supplierActivityFk: 'Supplier activity',
|
||||
healthRegister: 'Health register',
|
||||
street: 'Street',
|
||||
postcode: 'Postcode',
|
||||
city: 'City *',
|
||||
provinceFk: 'Province',
|
||||
country: 'Country',
|
||||
isTrucker: 'Trucker',
|
||||
isVies: 'Vies',
|
||||
},
|
||||
billingData: {
|
||||
payMethodFk: 'Billing data',
|
||||
payDemFk: 'Payment deadline',
|
||||
payDay: 'Pay day',
|
||||
},
|
||||
accounts: {
|
||||
iban: 'Iban',
|
||||
bankEntity: 'Bank entity',
|
||||
beneficiary: 'Beneficiary',
|
||||
},
|
||||
contacts: {
|
||||
name: 'Name',
|
||||
phone: 'Phone',
|
||||
mobile: 'Mobile',
|
||||
email: 'Email',
|
||||
observation: 'Notes',
|
||||
},
|
||||
addresses: {
|
||||
street: 'Street',
|
||||
postcode: 'Postcode',
|
||||
phone: 'Phone',
|
||||
name: 'Name',
|
||||
city: 'City',
|
||||
province: 'Province',
|
||||
mobile: 'Mobile',
|
||||
},
|
||||
agencyTerms: {
|
||||
agencyFk: 'Agency',
|
||||
minimumM3: 'Minimum M3',
|
||||
packagePrice: 'Package Price',
|
||||
kmPrice: 'Km Price',
|
||||
m3Price: 'M3 Price',
|
||||
routePrice: 'Route price',
|
||||
minimumKm: 'Minimum Km',
|
||||
addRow: 'Add row',
|
||||
},
|
||||
consumption: {
|
||||
entry: 'Entry',
|
||||
date: 'Date',
|
||||
reference: 'Reference',
|
||||
},
|
||||
},
|
||||
travel: {
|
||||
pageTitles: {
|
||||
|
|
|
@ -261,10 +261,88 @@ export default {
|
|||
entries: 'Entradas',
|
||||
list: 'Listado',
|
||||
summary: 'Resumen',
|
||||
create: 'Crear',
|
||||
basicData: 'Datos básicos',
|
||||
buys: 'Compras',
|
||||
notes: 'Notas',
|
||||
log: 'Historial',
|
||||
},
|
||||
list: {
|
||||
newEntry: 'Nueva entrada',
|
||||
landed: 'F. entrega',
|
||||
invoiceNumber: 'Núm. factura',
|
||||
supplier: 'Proveedor',
|
||||
booked: 'Asentado',
|
||||
confirmed: 'Confirmado',
|
||||
ordered: 'Pedida',
|
||||
},
|
||||
summary: {
|
||||
commission: 'Comisión',
|
||||
currency: 'Moneda',
|
||||
company: 'Empresa',
|
||||
reference: 'Referencia',
|
||||
invoiceNumber: 'Núm. factura',
|
||||
ordered: 'Pedida',
|
||||
confirmed: 'Confirmado',
|
||||
booked: 'Asentado',
|
||||
raid: 'Redada',
|
||||
excludedFromAvailable: 'Inventario',
|
||||
travelReference: 'Referencia',
|
||||
travelAgency: 'Agencia',
|
||||
travelShipped: 'F. envio',
|
||||
travelWarehouseOut: 'Alm. salida',
|
||||
travelDelivered: 'Enviada',
|
||||
travelLanded: 'F. entrega',
|
||||
travelWarehouseIn: 'Alm. entrada',
|
||||
travelReceived: 'Recibida',
|
||||
buys: 'Compras',
|
||||
quantity: 'Cantidad',
|
||||
stickers: 'Etiquetas',
|
||||
package: 'Embalaje',
|
||||
weight: 'Peso',
|
||||
packing: 'Packing',
|
||||
grouping: 'Grouping',
|
||||
buyingValue: 'Coste',
|
||||
import: 'Importe',
|
||||
pvp: 'PVP',
|
||||
item: 'Artículo',
|
||||
},
|
||||
basicData: {
|
||||
supplier: 'Proveedor',
|
||||
travel: 'Envío',
|
||||
reference: 'Referencia',
|
||||
invoiceNumber: 'Núm. factura',
|
||||
company: 'Empresa',
|
||||
currency: 'Moneda',
|
||||
observation: 'Observación',
|
||||
commission: 'Comisión',
|
||||
ordered: 'Pedida',
|
||||
confirmed: 'Confirmado',
|
||||
booked: 'Asentado',
|
||||
raid: 'Redada',
|
||||
excludedFromAvailable: 'Inventario',
|
||||
},
|
||||
buys: {
|
||||
groupingPrice: 'Precio grouping',
|
||||
packingPrice: 'Precio packing',
|
||||
reference: 'Referencia',
|
||||
observations: 'Observaciónes',
|
||||
item: 'Artículo',
|
||||
description: 'Descripción',
|
||||
size: 'Medida',
|
||||
packing: 'Packing',
|
||||
grouping: 'Grouping',
|
||||
buyingValue: 'Coste',
|
||||
packagingFk: 'Embalaje',
|
||||
file: 'Fichero',
|
||||
},
|
||||
notes: {
|
||||
observationType: 'Tipo de observación',
|
||||
description: 'Descripción',
|
||||
},
|
||||
descriptor: {
|
||||
agency: 'Agencia',
|
||||
landed: 'F. entrega',
|
||||
warehouseOut: 'Alm. salida',
|
||||
},
|
||||
},
|
||||
ticket: {
|
||||
|
@ -806,6 +884,10 @@ export default {
|
|||
pageTitles: {
|
||||
routes: 'Rutas',
|
||||
cmrsList: 'Listado de CMRs externos',
|
||||
RouteList: 'Listado',
|
||||
create: 'Crear',
|
||||
basicData: 'Datos básicos',
|
||||
summary: 'Summary',
|
||||
},
|
||||
cmr: {
|
||||
list: {
|
||||
|
@ -876,6 +958,72 @@ export default {
|
|||
create: {
|
||||
supplierName: 'Nombre del proveedor',
|
||||
},
|
||||
basicData: {
|
||||
alias: 'Alias',
|
||||
workerFk: 'Responsable',
|
||||
isSerious: 'Verificado',
|
||||
isActive: 'Activo',
|
||||
isPayMethodChecked: 'Método de pago validado',
|
||||
note: 'Notas',
|
||||
},
|
||||
fiscalData: {
|
||||
name: 'Razón social *',
|
||||
nif: 'NIF/CIF *',
|
||||
account: 'Cuenta',
|
||||
sageTaxTypeFk: 'Tipo de impuesto sage',
|
||||
sageWithholdingFk: 'Retención sage',
|
||||
sageTransactionTypeFk: 'Tipo de transacción sage',
|
||||
supplierActivityFk: 'Actividad proveedor',
|
||||
healthRegister: 'Pasaporte sanitario',
|
||||
street: 'Calle',
|
||||
postcode: 'Código postal',
|
||||
city: 'Población *',
|
||||
provinceFk: 'Provincia',
|
||||
country: 'País',
|
||||
isTrucker: 'Transportista',
|
||||
isVies: 'Vies',
|
||||
},
|
||||
billingData: {
|
||||
payMethodFk: 'Forma de pago',
|
||||
payDemFk: 'Plazo de pago',
|
||||
payDay: 'Día de pago',
|
||||
},
|
||||
accounts: {
|
||||
iban: 'Iban',
|
||||
bankEntity: 'Entidad bancaria',
|
||||
beneficiary: 'Beneficiario',
|
||||
},
|
||||
contacts: {
|
||||
name: 'Nombre',
|
||||
phone: 'Teléfono',
|
||||
mobile: 'Móvil',
|
||||
email: 'Email',
|
||||
observation: 'Notas',
|
||||
},
|
||||
addresses: {
|
||||
street: 'Dirección',
|
||||
postcode: 'Código postal',
|
||||
phone: 'Teléfono',
|
||||
name: 'Nombre',
|
||||
city: 'Población',
|
||||
province: 'Provincia',
|
||||
mobile: 'Móvil',
|
||||
},
|
||||
agencyTerms: {
|
||||
agencyFk: 'Agencia',
|
||||
minimumM3: 'M3 mínimos',
|
||||
packagePrice: 'Precio bulto',
|
||||
kmPrice: 'Precio Km',
|
||||
m3Price: 'Precio M3',
|
||||
routePrice: 'Precio ruta',
|
||||
minimumKm: 'Km mínimos',
|
||||
addRow: 'Añadir fila',
|
||||
},
|
||||
consumption: {
|
||||
entry: 'Entrada',
|
||||
date: 'Fecha',
|
||||
reference: 'Referencia',
|
||||
},
|
||||
},
|
||||
travel: {
|
||||
pageTitles: {
|
||||
|
|
|
@ -1,3 +1,130 @@
|
|||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const payMethods = ref([]);
|
||||
const bankEntitiesOptions = ref([]);
|
||||
const bankEntitiesRef = ref(null);
|
||||
|
||||
const filter = {
|
||||
fields: ['id', 'bic', 'name'],
|
||||
order: 'bic ASC',
|
||||
limit: 30,
|
||||
};
|
||||
|
||||
const getBankEntities = () => {
|
||||
bankEntitiesRef.value.fetch();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">Billing data</div>
|
||||
<fetch-data @on-fetch="(data) => (payMethods = data)" auto-load url="PayMethods" />
|
||||
<fetch-data
|
||||
ref="bankEntitiesRef"
|
||||
@on-fetch="(data) => (bankEntitiesOptions = data)"
|
||||
:filter="filter"
|
||||
auto-load
|
||||
url="BankEntities"
|
||||
/>
|
||||
|
||||
<FormModel
|
||||
:url-update="`Clients/${route.params.id}`"
|
||||
:url="`Clients/${route.params.id}/getCard`"
|
||||
auto-load
|
||||
model="customer"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Billing data')"
|
||||
:options="payMethods"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.payMethod"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('Due day')"
|
||||
:rules="validate('client.socialName')"
|
||||
v-model="data.dueDay"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('IBAN')" v-model="data.iban" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('Swift / BIC')"
|
||||
:options="bankEntitiesOptions"
|
||||
:roles-allowed-to-create="['salesAssistant', 'hr']"
|
||||
:rules="validate('Worker.bankEntity')"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.bankEntityFk"
|
||||
>
|
||||
<template #form>
|
||||
<CreateBankEntityForm @on-data-saved="getBankEntities()" />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel
|
||||
>{{ scope.opt.bic }}
|
||||
{{ scope.opt.name }}</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Received LCR')" v-model="data.hasLcr" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('VNL core received')"
|
||||
v-model="data.hasCoreVnl"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('VNL B2B received')" v-model="data.hasSepaVnl" />
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Billing data: Forma de pago
|
||||
Due day: Vencimiento
|
||||
IBAN: IBAN
|
||||
Swift / BIC: Swift / BIC
|
||||
Received LCR: Recibido LCR
|
||||
VNL core received: Recibido core VNL
|
||||
VNL B2B received: Recibido B2B VNL
|
||||
</i18n>
|
||||
|
|
|
@ -1,3 +1,176 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const provincesLocation = ref([]);
|
||||
|
||||
const consigneeFilter = {
|
||||
fields: [
|
||||
'id',
|
||||
'isDefaultAddress',
|
||||
'isActive',
|
||||
'nickname',
|
||||
'street',
|
||||
'city',
|
||||
'provinceFk',
|
||||
'phone',
|
||||
'mobile',
|
||||
'isEqualizated',
|
||||
'isLogifloraAllowed',
|
||||
'postalCode',
|
||||
],
|
||||
order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'],
|
||||
include: [
|
||||
{
|
||||
relation: 'observations',
|
||||
scope: {
|
||||
include: 'observationType',
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'province',
|
||||
scope: {
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const setProvince = (provinceFk) => {
|
||||
const result = provincesLocation.value.filter(
|
||||
(province) => province.id === provinceFk
|
||||
);
|
||||
return result[0]?.name || '';
|
||||
};
|
||||
|
||||
const toCustomerConsigneeCreate = () => {
|
||||
router.push({ name: 'CustomerConsigneeCreate' });
|
||||
};
|
||||
|
||||
const toCustomerConsigneeEdit = (consigneeId) => {
|
||||
router.push({
|
||||
name: 'CustomerConsigneeEdit',
|
||||
params: {
|
||||
id: route.params.id,
|
||||
consigneeId,
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">Consignees</div>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (provincesLocation = data)"
|
||||
auto-load
|
||||
url="Provinces/location"
|
||||
/>
|
||||
|
||||
<QCard class="q-pa-lg">
|
||||
<VnPaginate
|
||||
data-key="CustomerConsignees"
|
||||
:url="`Clients/${route.params.id}/addresses`"
|
||||
order="id"
|
||||
auto-load
|
||||
:filter="consigneeFilter"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QCard
|
||||
v-for="(item, index) in rows"
|
||||
:key="index"
|
||||
:class="{
|
||||
'consignees-card': true,
|
||||
'q-mb-md': index < rows.length - 1,
|
||||
}"
|
||||
@click="toCustomerConsigneeEdit(item.id)"
|
||||
>
|
||||
<div class="q-ml-xs q-mr-md flex items-center">
|
||||
<QIcon name="star" size="md" color="primary" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-weight-bold q-mb-sm">
|
||||
{{ item.nickname }} - #{{ item.id }}
|
||||
</div>
|
||||
<div>{{ item.street }}</div>
|
||||
<div>
|
||||
{{ item.postalCode }} - {{ item.city }},
|
||||
{{ setProvince(item.provinceFk) }}
|
||||
</div>
|
||||
<div class="flex">
|
||||
<QCheckbox
|
||||
:label="t('Is equalizated')"
|
||||
v-model="item.isEqualizated"
|
||||
class="q-mr-lg"
|
||||
disable
|
||||
/>
|
||||
<QCheckbox
|
||||
:label="t('Is logiflora allowed')"
|
||||
v-model="item.isLogifloraAllowed"
|
||||
disable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<QSeparator
|
||||
class="q-mx-lg"
|
||||
v-if="item.observations.length"
|
||||
vertical
|
||||
/>
|
||||
|
||||
<div v-if="item.observations.length">
|
||||
<div
|
||||
:key="index"
|
||||
class="flex q-mb-sm"
|
||||
v-for="(observation, index) in item.observations"
|
||||
>
|
||||
<div class="text-weight-bold q-mr-sm">
|
||||
{{ observation.observationType.description }}:
|
||||
</div>
|
||||
<div>{{ observation.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</QCard>
|
||||
|
||||
<QPageSticky :offset="[18, 18]">
|
||||
<QBtn
|
||||
@click.stop="toCustomerConsigneeCreate()"
|
||||
color="primary"
|
||||
fab
|
||||
icon="add"
|
||||
/>
|
||||
<QTooltip>
|
||||
{{ t('New consignee') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</QCard>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.consignees-card {
|
||||
border: 2px solid var(--vn-light-gray);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--vn-light-gray);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Is equalizated: Recargo de equivalencia
|
||||
Is logiflora allowed: Compra directa en Holanda
|
||||
New consignee: Nuevo consignatario
|
||||
</i18n>
|
||||
|
|
|
@ -1,3 +1,148 @@
|
|||
<script setup>
|
||||
import { ref, computed, onBeforeMount, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { date, QBtn } from 'quasar';
|
||||
|
||||
import { toCurrency, toDate } from 'src/filters';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const stateStore = useStateStore();
|
||||
|
||||
const arrayData = ref(null);
|
||||
const workerId = ref(0);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: { relation: 'user', scope: { fields: ['name'] } },
|
||||
},
|
||||
},
|
||||
],
|
||||
where: { clientFk: `${route.params.id}` },
|
||||
order: ['created DESC'],
|
||||
limit: 20,
|
||||
};
|
||||
arrayData.value = useArrayData('CustomerCreditsCard', {
|
||||
url: 'ClientCredits',
|
||||
filter,
|
||||
});
|
||||
await arrayData.value.fetch({ append: false });
|
||||
stateStore.rightDrawer = true;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const filteredColumns = columns.value.filter(
|
||||
(col) => col.name !== 'actions' && col.name !== 'customerStatus'
|
||||
);
|
||||
allColumnNames.value = filteredColumns.map((col) => col.name);
|
||||
});
|
||||
|
||||
const rows = computed(() => arrayData.value.store.data);
|
||||
const allColumnNames = ref([]);
|
||||
|
||||
const tableColumnComponents = {
|
||||
created: {
|
||||
component: 'span',
|
||||
props: () => {},
|
||||
event: () => {},
|
||||
},
|
||||
employee: {
|
||||
component: QBtn,
|
||||
props: () => ({ flat: true, color: 'blue' }),
|
||||
event: (prop) => {
|
||||
selectWorkerId(prop.row.clientFk);
|
||||
},
|
||||
},
|
||||
amount: {
|
||||
component: 'span',
|
||||
props: () => {},
|
||||
event: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
field: 'created',
|
||||
label: t('Since'),
|
||||
name: 'created',
|
||||
format: (value) => date.formatDate(value, 'DD/MM/YYYY hh:mm:ss'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
field: (value) => value.worker.user.name,
|
||||
label: t('Employee'),
|
||||
name: 'employee',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
field: 'amount',
|
||||
label: t('Credit'),
|
||||
name: 'amount',
|
||||
format: (value) => toCurrency(value),
|
||||
},
|
||||
]);
|
||||
|
||||
const selectWorkerId = (id) => {
|
||||
workerId.value = id;
|
||||
};
|
||||
|
||||
const toCustomerCreditCreate = () => {
|
||||
router.push({ name: 'CustomerCreditCreate' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">Credits</div>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<QTable
|
||||
:columns="columns"
|
||||
:pagination="{ rowsPerPage: 12 }"
|
||||
:rows="rows"
|
||||
class="full-width q-mt-md"
|
||||
row-key="id"
|
||||
>
|
||||
<template #body-cell="props">
|
||||
<QTd :props="props">
|
||||
<QTr :props="props" class="cursor-pointer">
|
||||
<component
|
||||
:is="tableColumnComponents[props.col.name].component"
|
||||
@click="tableColumnComponents[props.col.name].event(props)"
|
||||
class="rounded-borders q-pa-sm"
|
||||
v-bind="tableColumnComponents[props.col.name].props(props)"
|
||||
>
|
||||
{{ props.value }}
|
||||
<WorkerDescriptorProxy :id="workerId" />
|
||||
</component>
|
||||
</QTr>
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
</QPage>
|
||||
|
||||
<QPageSticky :offset="[18, 18]">
|
||||
<QBtn @click.stop="toCustomerCreditCreate()" color="primary" fab icon="add" />
|
||||
<QTooltip>
|
||||
{{ t('New credit') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Since: Desde
|
||||
Employee: Empleado
|
||||
Credit: Credito
|
||||
New credit: Nuevo credito
|
||||
</i18n>
|
||||
|
|
|
@ -1,3 +1,295 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const townsFetchDataRef = ref(null);
|
||||
const postcodeFetchDataRef = ref(null);
|
||||
const typesTaxes = ref([]);
|
||||
const typesTransactions = ref([]);
|
||||
const citiesLocationOptions = ref([]);
|
||||
const provincesLocationOptions = ref([]);
|
||||
const countriesOptions = ref([]);
|
||||
const postcodesOptions = ref([]);
|
||||
|
||||
const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formData) => {
|
||||
await postcodeFetchDataRef.value.fetch();
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.postcode = code;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||
formData.countryFk = countryFk;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">Fiscal data</div>
|
||||
<fetch-data auto-load @on-fetch="(data) => (typesTaxes = data)" url="SageTaxTypes" />
|
||||
<fetch-data
|
||||
auto-load
|
||||
@on-fetch="(data) => (typesTransactions = data)"
|
||||
url="SageTransactionTypes"
|
||||
/>
|
||||
<FetchData
|
||||
ref="townsFetchDataRef"
|
||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Towns/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Provinces/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (countriesOptions = data)"
|
||||
auto-load
|
||||
url="Countries"
|
||||
/>
|
||||
<FetchData
|
||||
ref="postcodeFetchDataRef"
|
||||
url="Postcodes/location"
|
||||
@on-fetch="(data) => (postcodesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FormModel
|
||||
:url-update="`Clients/${route.params.id}/updateFiscalData`"
|
||||
:url="`Clients/${route.params.id}/getCard`"
|
||||
auto-load
|
||||
model="customer"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('Social name')"
|
||||
:required="true"
|
||||
:rules="validate('client.socialName')"
|
||||
v-model="data.socialName"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Tax number')" v-model="data.fi" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('Street')" v-model="data.street" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Sage tax type')"
|
||||
:options="typesTaxes"
|
||||
hide-selected
|
||||
option-label="vat"
|
||||
option-value="id"
|
||||
v-model="data.sageTaxTypeFk"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Sage transaction type')"
|
||||
:options="typesTransactions"
|
||||
hide-selected
|
||||
option-label="vat"
|
||||
option-value="id"
|
||||
v-model="data.sageTransactionTypeFk"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('Postcode')"
|
||||
:options="postcodesOptions"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:rules="validate('Worker.postcode')"
|
||||
hide-selected
|
||||
option-label="code"
|
||||
option-value="code"
|
||||
v-model="data.postcode"
|
||||
>
|
||||
<template #form>
|
||||
<CustomerCreateNewPostcode
|
||||
@on-data-saved="onPostcodeCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||
<QItemLabel caption
|
||||
>{{ scope.opt.code }} -
|
||||
{{ scope.opt.town.name }} ({{
|
||||
scope.opt.town.province.name
|
||||
}},
|
||||
{{
|
||||
scope.opt.town.province.country.country
|
||||
}})</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('City')"
|
||||
:options="citiesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="name"
|
||||
v-model="data.city"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{
|
||||
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
||||
}}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Province')"
|
||||
:options="provincesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.provinceFk"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt.name} (${scope.opt.country.country})`
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Country')"
|
||||
:options="countriesOptions"
|
||||
hide-selected
|
||||
option-label="country"
|
||||
option-value="id"
|
||||
v-model="data.countryFk"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Active')" v-model="data.isActive" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Frozen')" v-model="data.isFreezed" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Has to invoice')" v-model="data.hasToInvoice" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Vies')" v-model="data.isVies" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Notify by email')"
|
||||
v-model="data.isToBeMailed"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Invoice by address')"
|
||||
v-model="data.hasToInvoiceByAddress"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Is equalizated')"
|
||||
v-model="data.isEqualizated"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Verified data')"
|
||||
v-model="data.isTaxDataChecked"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Incoterms authorization')"
|
||||
v-model="data.hasIncoterms"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Electronic invoice')"
|
||||
v-model="data.hasElectronicInvoice"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Social name: Razón social
|
||||
Tax number: NIF / CIF
|
||||
Street: Dirección fiscal
|
||||
Sage tax type: Tipo de impuesto Sage
|
||||
Sage transaction type: Tipo de transacción Sage
|
||||
Postcode: Código postal
|
||||
City: Población
|
||||
Province: Provincia
|
||||
Country: País
|
||||
Active: Activo
|
||||
Frozen: Congelado
|
||||
Has to invoice: Factura
|
||||
Vies: Vies
|
||||
Notify by email: Notificar vía e-mail
|
||||
Invoice by address: Facturar por consignatario
|
||||
Is equalizated: Recargo de equivalencia
|
||||
Verified data: Datos comprobados
|
||||
Incoterms authorization: Autorización incoterms
|
||||
Electronic invoice: Factura electrónica
|
||||
</i18n>
|
||||
|
|
|
@ -1,3 +1,96 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { date } from 'quasar';
|
||||
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const noteFilter = {
|
||||
order: 'created DESC',
|
||||
where: {
|
||||
clientFk: `${route.params.id}`,
|
||||
},
|
||||
};
|
||||
|
||||
const toCustomerNoteCreate = () => {
|
||||
router.push({ name: 'CustomerNoteCreate' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">Notes</div>
|
||||
<QCard class="q-pa-lg">
|
||||
<VnPaginate
|
||||
data-key="CustomerNotes"
|
||||
:url="'clientObservations'"
|
||||
auto-load
|
||||
:filter="noteFilter"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<div v-if="rows.length">
|
||||
<QCard
|
||||
v-for="(item, index) in rows"
|
||||
:key="index"
|
||||
:class="{
|
||||
'q-pa-md': true,
|
||||
'q-rounded': true,
|
||||
'custom-border': true,
|
||||
'q-mb-md': index < rows.length - 1,
|
||||
}"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<p class="label-color">{{ item.worker.user.nickname }}</p>
|
||||
<p class="label-color">
|
||||
{{
|
||||
date.formatDate(item?.created, 'DD-MM-YYYY HH:mm:ss')
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<h6 class="q-mt-xs q-mb-none">{{ item.text }}</h6>
|
||||
</QCard>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<h5 class="flex justify-center label-color">
|
||||
{{ t('globals.noResults') }}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<QPageSticky :offset="[18, 18]">
|
||||
<QBtn
|
||||
@click.stop="toCustomerConsigneeCreate()"
|
||||
color="primary"
|
||||
fab
|
||||
icon="add"
|
||||
/>
|
||||
<QTooltip>
|
||||
{{ t('New consignee') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</QCard>
|
||||
|
||||
<QPageSticky :offset="[18, 18]">
|
||||
<QBtn @click.stop="toCustomerNoteCreate()" color="primary" fab icon="add" />
|
||||
<QTooltip>
|
||||
{{ t('New consignee') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.custom-border {
|
||||
border: 2px solid var(--vn-light-gray);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.label-color {
|
||||
color: var(--vn-label);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -30,6 +30,7 @@ const newClientForm = reactive({
|
|||
});
|
||||
|
||||
const postcodeFetchDataRef = ref(null);
|
||||
const townsFetchDataRef = ref(null);
|
||||
const workersOptions = ref([]);
|
||||
const businessTypesOptions = ref([]);
|
||||
const citiesLocationOptions = ref([]);
|
||||
|
@ -37,8 +38,13 @@ const provincesLocationOptions = ref([]);
|
|||
const countriesOptions = ref([]);
|
||||
const postcodesOptions = ref([]);
|
||||
|
||||
const onPostcodeCreated = async () => {
|
||||
postcodeFetchDataRef.value.fetch();
|
||||
const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formData) => {
|
||||
await postcodeFetchDataRef.value.fetch();
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.postcode = code;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||
formData.countryFk = countryFk;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -60,6 +66,7 @@ const onPostcodeCreated = async () => {
|
|||
url="BusinessTypes"
|
||||
/>
|
||||
<FetchData
|
||||
ref="townsFetchDataRef"
|
||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Towns/location"
|
||||
|
@ -78,7 +85,6 @@ const onPostcodeCreated = async () => {
|
|||
<VnSubToolbar />
|
||||
<FormModel
|
||||
:form-initial-data="newClientForm"
|
||||
:observe-form-changes="false"
|
||||
model="client"
|
||||
url-create="Clients/createWithUser"
|
||||
>
|
||||
|
@ -145,7 +151,7 @@ const onPostcodeCreated = async () => {
|
|||
>
|
||||
<template #form>
|
||||
<CustomerCreateNewPostcode
|
||||
@on-data-saved="onPostcodeCreated($event)"
|
||||
@on-data-saved="onPostcodeCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { ref, computed, onBeforeMount } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { QBtn, QCheckbox } from 'quasar';
|
||||
import { QBtn, QCheckbox, useQuasar } from 'quasar';
|
||||
|
||||
import { toCurrency, toDate } from 'filters/index';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
|
@ -12,34 +12,19 @@ import CustomerNotificationsFilter from './CustomerDefaulterFilter.vue';
|
|||
import CustomerBalanceDueTotal from './CustomerBalanceDueTotal.vue';
|
||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import CustomerDefaulterAddObservation from './CustomerDefaulterAddObservation.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const arrayData = ref(null);
|
||||
const balanceDueTotal = ref(0);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
arrayData.value = useArrayData('CustomerDefaulter', {
|
||||
url: 'Defaulters/filter',
|
||||
limit: 0,
|
||||
});
|
||||
await arrayData.value.fetch({ append: false });
|
||||
balanceDueTotal.value = arrayData.value.store.data.reduce(
|
||||
(accumulator, currentValue) => {
|
||||
return accumulator + (currentValue['amount'] || 0);
|
||||
},
|
||||
0
|
||||
);
|
||||
console.log(balanceDueTotal.value);
|
||||
stateStore.rightDrawer = true;
|
||||
});
|
||||
|
||||
const rows = computed(() => arrayData.value.store.data);
|
||||
|
||||
const customerId = ref(0);
|
||||
const selected = ref([]);
|
||||
const workerId = ref(0);
|
||||
const customerId = ref(0);
|
||||
|
||||
const rows = computed(() => arrayData.value.store.data);
|
||||
|
||||
const tableColumnComponents = {
|
||||
client: {
|
||||
|
@ -49,11 +34,10 @@ const tableColumnComponents = {
|
|||
},
|
||||
isWorker: {
|
||||
component: QCheckbox,
|
||||
props: ({ value }) => ({
|
||||
props: ({ row }) => ({
|
||||
disable: true,
|
||||
'model-value': Boolean(value),
|
||||
'model-value': Boolean(row.selected),
|
||||
}),
|
||||
event: () => {},
|
||||
},
|
||||
salesperson: {
|
||||
component: QBtn,
|
||||
|
@ -171,6 +155,25 @@ const columns = computed(() => [
|
|||
},
|
||||
]);
|
||||
|
||||
onBeforeMount(() => {
|
||||
getArrayData();
|
||||
});
|
||||
|
||||
const getArrayData = async () => {
|
||||
arrayData.value = useArrayData('CustomerDefaulter', {
|
||||
url: 'Defaulters/filter',
|
||||
limit: 0,
|
||||
});
|
||||
await arrayData.value.fetch({ append: false });
|
||||
balanceDueTotal.value = arrayData.value.store.data.reduce(
|
||||
(accumulator, currentValue) => {
|
||||
return accumulator + (currentValue['amount'] || 0);
|
||||
},
|
||||
0
|
||||
);
|
||||
stateStore.rightDrawer = true;
|
||||
};
|
||||
|
||||
const selectCustomerId = (id) => {
|
||||
workerId.value = 0;
|
||||
customerId.value = id;
|
||||
|
@ -180,6 +183,20 @@ const selectWorkerId = (id) => {
|
|||
customerId.value = 0;
|
||||
workerId.value = id;
|
||||
};
|
||||
|
||||
const viewAddObservation = (rowsSelected) => {
|
||||
quasar.dialog({
|
||||
component: CustomerDefaulterAddObservation,
|
||||
componentProps: {
|
||||
clients: rowsSelected,
|
||||
promise: refreshData,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const refreshData = () => {
|
||||
getArrayData();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -190,11 +207,17 @@ const selectWorkerId = (id) => {
|
|||
</QDrawer>
|
||||
|
||||
<QToolbar class="bg-vn-dark">
|
||||
<div id="st-data">
|
||||
<div id="st-data" class="row">
|
||||
<CustomerBalanceDueTotal :amount="balanceDueTotal" />
|
||||
<div class="flex items-center q-ml-lg">
|
||||
<QBtn
|
||||
color="primary"
|
||||
icon="vn:notes"
|
||||
:disabled="!selected.length"
|
||||
@click.stop="viewAddObservation(selected)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<QSpace />
|
||||
<div id="st-actions"></div>
|
||||
</QToolbar>
|
||||
|
||||
<QPage class="column items-center q-pa-md">
|
||||
|
@ -204,7 +227,7 @@ const selectWorkerId = (id) => {
|
|||
:rows="rows"
|
||||
class="full-width q-mt-md"
|
||||
hide-bottom
|
||||
row-key="id"
|
||||
row-key="clientFk"
|
||||
selection="multiple"
|
||||
v-model:selected="selected"
|
||||
>
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
clients: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
promise: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const newObservation = ref(null);
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
const data = $props.clients.map((item) => {
|
||||
return { clientFk: item.clientFk, text: newObservation.value };
|
||||
});
|
||||
await axios.post('ClientObservations', data);
|
||||
|
||||
const payload = {
|
||||
defaulters: $props.clients,
|
||||
observation: newObservation.value,
|
||||
};
|
||||
await axios.post('Defaulters/observationEmail', payload);
|
||||
|
||||
await $props.promise();
|
||||
|
||||
quasar.notify({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
});
|
||||
} catch (error) {
|
||||
quasar.notify({
|
||||
message: t(`${error.message}`),
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QDialog ref="dialogRef">
|
||||
<QCard class="q-pa-md q-mb-md">
|
||||
<QCardSection>
|
||||
<QForm @submit="onSubmit()" class="q-pa-sm">
|
||||
<div>
|
||||
{{
|
||||
t('Add observation to all selected clients', {
|
||||
numberClients: t($props.clients.length),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('Message')"
|
||||
type="textarea"
|
||||
v-model="newObservation"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<div class="q-mt-lg row justify-end">
|
||||
<QBtn
|
||||
:label="t('globals.cancel')"
|
||||
color="primary"
|
||||
flat
|
||||
class="q-mr-md"
|
||||
v-close-popup
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.save')"
|
||||
type="submit"
|
||||
color="primary"
|
||||
v-close-popup
|
||||
/>
|
||||
</div>
|
||||
</QForm>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Add observation to all selected clients: Añadir observación a { numberClients } cliente(s) seleccionado(s)
|
||||
Message: Mensaje
|
||||
</i18n>
|
|
@ -485,7 +485,6 @@ const selectCustomerId = (id) => {
|
|||
};
|
||||
|
||||
const selectSalesPersonId = (id) => {
|
||||
console.log('selectedSalesPersonId:: ', selectedSalesPersonId.value);
|
||||
selectedSalesPersonId.value = id;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
<script setup>
|
||||
import { onBeforeMount, reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||
import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const formInitialData = reactive({ isDefaultAddress: false });
|
||||
|
||||
const townsFetchDataRef = ref(null);
|
||||
const postcodeFetchDataRef = ref(null);
|
||||
const urlCreate = ref('');
|
||||
|
||||
const postcodesOptions = ref([]);
|
||||
const citiesLocationOptions = ref([]);
|
||||
const provincesLocationOptions = ref([]);
|
||||
const agencyModes = ref([]);
|
||||
const incoterms = ref([]);
|
||||
const customsAgents = ref([]);
|
||||
|
||||
onBeforeMount(() => {
|
||||
urlCreate.value = `Clients/${route.params.id}/createAddress`;
|
||||
getCustomsAgents();
|
||||
});
|
||||
|
||||
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
||||
await postcodeFetchDataRef.value.fetch();
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.postalCode = code;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||
};
|
||||
|
||||
const getCustomsAgents = async () => {
|
||||
const { data } = await axios.get('CustomsAgents');
|
||||
customsAgents.value = data;
|
||||
};
|
||||
|
||||
const refreshData = () => {
|
||||
getCustomsAgents();
|
||||
};
|
||||
|
||||
const toCustomerConsignees = () => {
|
||||
router.push({
|
||||
name: 'CustomerConsignees',
|
||||
params: {
|
||||
id: route.params.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (postcodesOptions = data)"
|
||||
auto-load
|
||||
ref="postcodeFetchDataRef"
|
||||
url="Postcodes/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||
auto-load
|
||||
ref="townsFetchDataRef"
|
||||
url="Towns/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Provinces/location"
|
||||
/>
|
||||
<fetch-data
|
||||
@on-fetch="(data) => (agencyModes = data)"
|
||||
auto-load
|
||||
url="AgencyModes/isActive"
|
||||
/>
|
||||
<fetch-data @on-fetch="(data) => (incoterms = data)" auto-load url="Incoterms" />
|
||||
|
||||
<FormModel
|
||||
:form-initial-data="formInitialData"
|
||||
:observe-form-changes="false"
|
||||
:url-create="urlCreate"
|
||||
@on-data-saved="toCustomerConsignees()"
|
||||
model="client"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('Consignee')" v-model="data.nickname" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Street address')" v-model="data.street" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('Postcode')"
|
||||
:options="postcodesOptions"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:rules="validate('Worker.postcode')"
|
||||
hide-selected
|
||||
option-label="code"
|
||||
option-value="code"
|
||||
v-model="data.postalCode"
|
||||
>
|
||||
<template #form>
|
||||
<CustomerCreateNewPostcode
|
||||
@on-data-saved="onPostcodeCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt.code }} -
|
||||
{{ scope.opt.town.name }}
|
||||
({{ scope.opt.town.province.name }},
|
||||
{{ scope.opt.town.province.country.country }})
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
<div class="col">
|
||||
<!-- ciudades -->
|
||||
<VnSelectFilter
|
||||
:label="t('City')"
|
||||
:options="citiesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="name"
|
||||
v-model="data.city"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{
|
||||
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
||||
}}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Province')"
|
||||
:options="provincesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.provinceFk"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt.name} (${scope.opt.country.country})`
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Agency')"
|
||||
:options="agencyModes"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.agencyModeFk"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('Phone')" v-model="data.phone" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Mobile')" v-model="data.mobile" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Incoterms')"
|
||||
:options="incoterms"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="code"
|
||||
v-model="data.incotermsFk"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('Customs agent')"
|
||||
:options="customsAgents"
|
||||
hide-selected
|
||||
option-label="fiscalName"
|
||||
option-value="id"
|
||||
v-model="data.customsAgentFk"
|
||||
>
|
||||
<template #form>
|
||||
<CustomsNewCustomsAgent @on-data-saved="refreshData()" />
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-icon {
|
||||
cursor: pointer;
|
||||
background-color: $primary;
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Default: Predeterminado
|
||||
Consignee: Consignatario
|
||||
Street address: Dirección postal
|
||||
Postcode: Código postal
|
||||
City: Población
|
||||
Province: Provincia
|
||||
Agency: Agencia
|
||||
Phone: Teléfono
|
||||
Mobile: Movíl
|
||||
Incoterms: Incoterms
|
||||
Customs agent: Agente de aduanas
|
||||
</i18n>
|
|
@ -0,0 +1,371 @@
|
|||
<script setup>
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||
import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const townsFetchDataRef = ref(null);
|
||||
const postcodeFetchDataRef = ref(null);
|
||||
const urlUpdate = ref('');
|
||||
|
||||
const postcodesOptions = ref([]);
|
||||
const citiesLocationOptions = ref([]);
|
||||
const provincesLocationOptions = ref([]);
|
||||
const agencyModes = ref([]);
|
||||
const incoterms = ref([]);
|
||||
const customsAgents = ref([]);
|
||||
const observationTypes = ref([]);
|
||||
const notes = ref([]);
|
||||
|
||||
onBeforeMount(() => {
|
||||
urlUpdate.value = `Clients/${route.params.id}/updateAddress/${route.params.consigneeId}`;
|
||||
});
|
||||
|
||||
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
||||
await postcodeFetchDataRef.value.fetch();
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.postalCode = code;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||
};
|
||||
|
||||
const getData = async (observations) => {
|
||||
observationTypes.value = observations;
|
||||
|
||||
if (observationTypes.value.length) {
|
||||
const filter = {
|
||||
fields: ['id', 'addressFk', 'observationTypeFk', 'description'],
|
||||
where: { addressFk: `${route.params.consigneeId}` },
|
||||
};
|
||||
const { data } = await axios.get('AddressObservations', {
|
||||
params: { filter: JSON.stringify(filter) },
|
||||
});
|
||||
|
||||
if (data.length) {
|
||||
notes.value = data
|
||||
.map((observation) => {
|
||||
const type = observationTypes.value.find(
|
||||
(type) => type.id === observation.observationTypeFk
|
||||
);
|
||||
return type
|
||||
? {
|
||||
$isNew: false,
|
||||
$oldData: null,
|
||||
$orgIndex: null,
|
||||
addressFk: `${route.params.consigneeId}`,
|
||||
description: observation.description,
|
||||
observationTypeFk: type.id,
|
||||
}
|
||||
: null;
|
||||
})
|
||||
.filter((item) => item !== null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addNote = () => {
|
||||
notes.value.push({
|
||||
$isNew: true,
|
||||
$oldData: null,
|
||||
$orgIndex: null,
|
||||
addressFk: `${route.params.consigneeId}`,
|
||||
description: '',
|
||||
observationTypeFk: '',
|
||||
});
|
||||
};
|
||||
|
||||
const deleteNote = (index) => {
|
||||
notes.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const onDataSaved = () => {
|
||||
const payload = {
|
||||
creates: notes.value,
|
||||
};
|
||||
axios.post('AddressObservations/crud', payload);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
ref="postcodeFetchDataRef"
|
||||
@on-fetch="(data) => (postcodesOptions = data)"
|
||||
auto-load
|
||||
url="Postcodes/location"
|
||||
/>
|
||||
<FetchData
|
||||
ref="townsFetchDataRef"
|
||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Towns/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Provinces/location"
|
||||
/>
|
||||
<fetch-data
|
||||
@on-fetch="(data) => (agencyModes = data)"
|
||||
auto-load
|
||||
url="AgencyModes/isActive"
|
||||
/>
|
||||
<fetch-data @on-fetch="(data) => (incoterms = data)" auto-load url="Incoterms" />
|
||||
<fetch-data
|
||||
@on-fetch="(data) => (customsAgents = data)"
|
||||
auto-load
|
||||
url="CustomsAgents"
|
||||
/>
|
||||
<fetch-data @on-fetch="getData" auto-load url="ObservationTypes" />
|
||||
|
||||
<FormModel
|
||||
:observe-form-changes="false"
|
||||
:url-update="urlUpdate"
|
||||
:url="`Addresses/${route.params.consigneeId}`"
|
||||
@on-data-saved="onDataSaved()"
|
||||
auto-load
|
||||
model="client"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('Enabled')" v-model="data.isActive" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Is equalizated')"
|
||||
v-model="data.isEqualizated"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
:label="t('Is Loginflora allowed')"
|
||||
v-model="data.isLogifloraAllowed"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('Consignee')" v-model="data.nickname" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Street address')" v-model="data.street" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('Postcode')"
|
||||
:options="postcodesOptions"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:rules="validate('Worker.postcode')"
|
||||
hide-selected
|
||||
option-label="code"
|
||||
option-value="code"
|
||||
v-model="data.postalCode"
|
||||
>
|
||||
<template #form>
|
||||
<CustomerCreateNewPostcode
|
||||
@on-data-saved="onPostcodeCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt.code }} -
|
||||
{{ scope.opt.town.name }}
|
||||
({{ scope.opt.town.province.name }},
|
||||
{{ scope.opt.town.province.country.country }})
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
<div class="col">
|
||||
<!-- ciudades -->
|
||||
<VnSelectFilter
|
||||
:label="t('City')"
|
||||
:options="citiesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="name"
|
||||
v-model="data.city"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{
|
||||
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
||||
}}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Province')"
|
||||
:options="provincesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.provinceFk"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt.name} (${scope.opt.country.country})`
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Agency')"
|
||||
:options="agencyModes"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.agencyModeFk"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('Phone')" v-model="data.phone" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Mobile')" v-model="data.mobile" />
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Incoterms')"
|
||||
:options="incoterms"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="code"
|
||||
v-model="data.incotermsFk"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('Customs agent')"
|
||||
:options="customsAgents"
|
||||
hide-selected
|
||||
option-label="fiscalName"
|
||||
option-value="id"
|
||||
v-model="data.customsAgentFk"
|
||||
>
|
||||
<template #form>
|
||||
<CustomsNewCustomsAgent />
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<h4 class="q-mb-xs">{{ t('Notes') }}</h4>
|
||||
<VnRow
|
||||
:key="index"
|
||||
class="row q-gutter-md q-mb-md"
|
||||
v-for="(note, index) in notes"
|
||||
>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Observation type')"
|
||||
:options="observationTypes"
|
||||
hide-selected
|
||||
option-label="description"
|
||||
option-value="id"
|
||||
v-model="note.observationTypeFk"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Description')" v-model="note.description" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<QIcon
|
||||
@click.stop="deleteNote(index)"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
name="delete"
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Remove') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</VnRow>
|
||||
|
||||
<QIcon
|
||||
@click.stop="addNote()"
|
||||
class="cursor-pointer add-icon q-mt-md"
|
||||
name="add"
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Add note') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-icon {
|
||||
background-color: $primary;
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Enabled: Activo
|
||||
Is equalizated: Recargo de equivalencia
|
||||
Is Loginflora allowed: Compra directa en Holanda
|
||||
Consignee: Consignatario
|
||||
Street address: Dirección postal
|
||||
Postcode: Código postal
|
||||
City: Población
|
||||
Province: Provincia
|
||||
Agency: Agencia
|
||||
Phone: Teléfono
|
||||
Mobile: Movíl
|
||||
Incoterms: Incoterms
|
||||
Customs agent: Agente de aduanas
|
||||
Notes: Notas
|
||||
Observation type: Tipo de observación
|
||||
Description: Descripción
|
||||
Add note: Añadir nota
|
||||
Remove note: Eliminar nota
|
||||
</i18n>
|
|
@ -0,0 +1,51 @@
|
|||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const initialData = reactive({
|
||||
credit: null,
|
||||
});
|
||||
|
||||
const toCustomerCredits = () => {
|
||||
router.push({
|
||||
name: 'CustomerCredits',
|
||||
params: {
|
||||
id: route.params.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormModel
|
||||
:form-initial-data="initialData"
|
||||
:observe-form-changes="false"
|
||||
:url-update="`/Clients/${route.params.id}`"
|
||||
@on-data-saved="toCustomerCredits()"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('Credit')"
|
||||
type="number"
|
||||
v-model.number="data.credit"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Credit: Crédito
|
||||
</i18n>
|
|
@ -0,0 +1,65 @@
|
|||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import FormModelPopup from 'src/components/FormModelPopup.vue';
|
||||
|
||||
const emit = defineEmits(['onDataSaved']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const initialData = reactive({
|
||||
nif: null,
|
||||
fiscalName: null,
|
||||
street: null,
|
||||
phone: null,
|
||||
});
|
||||
|
||||
const onDataSaved = (dataSaved) => {
|
||||
emit('onDataSaved', dataSaved);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormModelPopup
|
||||
:title="t('New customs agent')"
|
||||
url-create="CustomsAgents"
|
||||
model="customer"
|
||||
:form-initial-data="initialData"
|
||||
@on-data-saved="onDataSaved($event)"
|
||||
>
|
||||
<template #form-inputs="{ data }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('NIF')" :required="true" v-model="data.nif" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('Fiscal name')"
|
||||
:required="true"
|
||||
v-model="data.fiscalName"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput :label="t('Street')" v-model="data.street" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput :label="t('Phone')" v-model="data.phone" />
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModelPopup>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
New customs agent: Nuevo agente de aduanas
|
||||
NIF: NIF
|
||||
Fiscal name: Nombre fiscal
|
||||
Street: Dirección fiscal
|
||||
Phone: Teléfono
|
||||
</i18n>
|
|
@ -0,0 +1,51 @@
|
|||
<script setup>
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const initialData = reactive({
|
||||
text: null,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
initialData.clientFk = `${route.params.id}`;
|
||||
});
|
||||
|
||||
const toCustomerNotes = () => {
|
||||
router.push({
|
||||
name: 'CustomerNotes',
|
||||
params: {
|
||||
id: route.params.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormModel
|
||||
:form-initial-data="initialData"
|
||||
:observe-form-changes="false"
|
||||
url-create="ClientObservations"
|
||||
@on-data-saved="toCustomerNotes()"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput :label="t('Note')" type="textarea" v-model="data.text" />
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Note: Nota
|
||||
</i18n>
|
|
@ -0,0 +1,202 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const suppliersOptions = ref([]);
|
||||
const travelsOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
const currenciesOptions = ref([]);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
order="nickname"
|
||||
@on-fetch="(data) => (suppliersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Travels/filter"
|
||||
:filter="{ fields: ['id', 'warehouseInName'] }"
|
||||
order="id"
|
||||
@on-fetch="(data) => (travelsOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="companiesRef"
|
||||
url="Companies"
|
||||
:filter="{ fields: ['id', 'code'] }"
|
||||
order="code"
|
||||
@on-fetch="(data) => (companiesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="currenciesRef"
|
||||
url="Currencies"
|
||||
:filter="{ fields: ['id', 'code'] }"
|
||||
order="code"
|
||||
@on-fetch="(data) => (currenciesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FormModel
|
||||
:url="`Entries/${route.params.id}`"
|
||||
:url-update="`Entries/${route.params.id}`"
|
||||
model="entry"
|
||||
auto-load
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('entry.basicData.supplier')"
|
||||
v-model="data.supplierFk"
|
||||
:options="suppliersOptions"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
hide-selected
|
||||
:required="true"
|
||||
map-options
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('entry.basicData.travel')"
|
||||
v-model="data.travelFk"
|
||||
:options="travelsOptions"
|
||||
option-value="id"
|
||||
option-label="warehouseInName"
|
||||
map-options
|
||||
hide-selected
|
||||
:required="true"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel
|
||||
>{{ scope.opt?.agencyModeName }} -
|
||||
{{ scope.opt?.warehouseInName }} ({{
|
||||
toDate(scope.opt?.shipped)
|
||||
}}) → {{ scope.opt?.warehouseOutName }} ({{
|
||||
toDate(scope.opt?.landed)
|
||||
}})</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.reference"
|
||||
:label="t('entry.basicData.reference')"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.invoiceNumber"
|
||||
:label="t('entry.basicData.invoiceNumber')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('entry.basicData.company')"
|
||||
v-model="data.companyFk"
|
||||
:options="companiesOptions"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
map-options
|
||||
hide-selected
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('entry.basicData.currency')"
|
||||
v-model="data.currencyFk"
|
||||
:options="currenciesOptions"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('entry.basicData.commission')"
|
||||
v-model="data.commission"
|
||||
type="number"
|
||||
autofocus
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('entry.basicData.observation')"
|
||||
type="textarea"
|
||||
v-model="data.observation"
|
||||
fill-input
|
||||
autogrow
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isOrdered"
|
||||
:label="t('entry.basicData.ordered')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isConfirmed"
|
||||
:label="t('entry.basicData.confirmed')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isExcludedFromAvailable"
|
||||
:label="t('entry.basicData.excludedFromAvailable')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isBooked"
|
||||
:label="t('entry.basicData.booked')"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
|
@ -0,0 +1,409 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const rowsSelected = ref([]);
|
||||
const entryBuysPaginateRef = ref(null);
|
||||
const packagingsOptions = ref(null);
|
||||
|
||||
const tableColumnComponents = computed(() => ({
|
||||
item: {
|
||||
component: () => 'span',
|
||||
props: () => {},
|
||||
event: () => ({}),
|
||||
},
|
||||
quantity: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
class: 'input-number',
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
packagingFk: {
|
||||
component: () => VnSelectFilter,
|
||||
props: () => ({
|
||||
'option-value': 'id',
|
||||
'option-label': 'id',
|
||||
'emit-value': true,
|
||||
'map-options': true,
|
||||
'use-input': true,
|
||||
'hide-selected': true,
|
||||
options: packagingsOptions.value,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'update:modelValue': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
stickers: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
class: 'input-number',
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
weight: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
packing: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
grouping: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
buyingValue: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
price2: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
price3: {
|
||||
component: () => VnInput,
|
||||
props: (col) => ({
|
||||
type: 'number',
|
||||
min: 0,
|
||||
label: col.label,
|
||||
}),
|
||||
event: (props) => ({
|
||||
'keyup.enter': () => saveChange(props.row),
|
||||
}),
|
||||
},
|
||||
import: {
|
||||
component: () => 'span',
|
||||
props: () => {},
|
||||
event: () => ({}),
|
||||
},
|
||||
}));
|
||||
|
||||
const entriesTableColumns = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: t('entry.summary.item'),
|
||||
field: 'id',
|
||||
name: 'item',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.quantity'),
|
||||
field: 'quantity',
|
||||
name: 'quantity',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.package'),
|
||||
field: 'packagingFk',
|
||||
name: 'packagingFk',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.stickers'),
|
||||
field: 'stickers',
|
||||
name: 'stickers',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.weight'),
|
||||
field: 'weight',
|
||||
name: 'weight',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.packing'),
|
||||
field: 'packing',
|
||||
name: 'packing',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.grouping'),
|
||||
field: 'grouping',
|
||||
name: 'grouping',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.buyingValue'),
|
||||
field: 'buyingValue',
|
||||
name: 'buyingValue',
|
||||
align: 'left',
|
||||
format: (value) => toCurrency(value),
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.groupingPrice'),
|
||||
field: 'price2',
|
||||
name: 'price2',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.packingPrice'),
|
||||
field: 'price3',
|
||||
name: 'price3',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.import'),
|
||||
name: 'import',
|
||||
align: 'left',
|
||||
format: (_, row) => toCurrency(row.buyingValue * row.quantity),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const saveChange = async (rowData) => {
|
||||
await axios.patch(`Buys/${rowData.id}`, rowData);
|
||||
};
|
||||
|
||||
const openRemoveDialog = async () => {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('Confirm deletion'),
|
||||
message: t(
|
||||
`Are you sure you want to delete this buy${
|
||||
rowsSelected.value.length > 1 ? 's' : ''
|
||||
}?`
|
||||
),
|
||||
data: rowsSelected.value,
|
||||
},
|
||||
})
|
||||
.onOk(async () => {
|
||||
try {
|
||||
await deleteBuys();
|
||||
const notifyMessage = t(
|
||||
`Buy${rowsSelected.value.length > 1 ? 's' : ''} deleted`
|
||||
);
|
||||
notify(notifyMessage, 'positive');
|
||||
} catch (err) {
|
||||
console.error('Error deleting buys');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deleteBuys = async () => {
|
||||
await axios.post('Buys/deleteBuys', { buys: rowsSelected.value });
|
||||
entryBuysPaginateRef.value.fetch();
|
||||
};
|
||||
|
||||
const importBuys = () => {
|
||||
router.push({ name: 'EntryBuysImport' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
ref="expensesRef"
|
||||
url="Packagings"
|
||||
:filter="{ fields: ['id'], where: { freightItemFk: true }, order: 'id ASC' }"
|
||||
auto-load
|
||||
@on-fetch="(data) => (packagingsOptions = data)"
|
||||
/>
|
||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||
<QBtnGroup push style="column-gap: 10px">
|
||||
<slot name="moreBeforeActions" />
|
||||
<QBtn
|
||||
:label="t('globals.remove')"
|
||||
color="primary"
|
||||
icon="delete"
|
||||
flat
|
||||
@click="openRemoveDialog()"
|
||||
:disable="!rowsSelected?.length"
|
||||
:title="t('globals.remove')"
|
||||
/>
|
||||
</QBtnGroup>
|
||||
</Teleport>
|
||||
<VnPaginate
|
||||
ref="entryBuysPaginateRef"
|
||||
data-key="EntryBuys"
|
||||
:url="`Entries/${route.params.id}/getBuys`"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QTable
|
||||
:rows="rows"
|
||||
:columns="entriesTableColumns"
|
||||
selection="multiple"
|
||||
row-key="id"
|
||||
hide-bottom
|
||||
class="full-width q-mt-md"
|
||||
:grid="$q.screen.lt.md"
|
||||
v-model:selected="rowsSelected"
|
||||
>
|
||||
<template #body="props">
|
||||
<QTr>
|
||||
<QTd>
|
||||
<QCheckbox v-model="props.selected" />
|
||||
</QTd>
|
||||
<QTd v-for="col in props.cols" :key="col.name">
|
||||
<component
|
||||
:is="tableColumnComponents[col.name].component()"
|
||||
v-bind="tableColumnComponents[col.name].props(col)"
|
||||
v-model="props.row[col.field]"
|
||||
v-on="tableColumnComponents[col.name].event(props)"
|
||||
>
|
||||
<template
|
||||
v-if="col.name === 'item' || col.name === 'import'"
|
||||
>
|
||||
{{ col.value }}
|
||||
</template>
|
||||
</component>
|
||||
</QTd>
|
||||
</QTr>
|
||||
<QTr no-hover>
|
||||
<QTd />
|
||||
<QTd>
|
||||
<span>{{ props.row.item.itemType.code }}</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span>{{ props.row.item.id }}</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span>{{ props.row.item.size }}</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span>{{ toCurrency(props.row.item.minPrice) }}</span>
|
||||
</QTd>
|
||||
<QTd colspan="7">
|
||||
<span>{{ props.row.item.concept }}</span>
|
||||
<span v-if="props.row.item.subName" class="subName">
|
||||
{{ props.row.item.subName }}
|
||||
</span>
|
||||
<fetched-tags :item="props.row.item" :max-length="5" />
|
||||
</QTd>
|
||||
</QTr>
|
||||
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
||||
<QTr v-if="props.rowIndex !== rows.length - 1" class="separation-row">
|
||||
<QTd colspan="12" style="height: 24px" />
|
||||
</QTr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||
<QCard bordered flat>
|
||||
<QCardSection>
|
||||
<QCheckbox v-model="props.selected" dense />
|
||||
</QCardSection>
|
||||
<QSeparator />
|
||||
<QList dense>
|
||||
<QItem v-for="col in props.cols" :key="col.name">
|
||||
<component
|
||||
:is="tableColumnComponents[col.name].component()"
|
||||
v-bind="
|
||||
tableColumnComponents[col.name].props(col)
|
||||
"
|
||||
v-model="props.row[col.field]"
|
||||
v-on="
|
||||
tableColumnComponents[col.name].event(props)
|
||||
"
|
||||
class="full-width"
|
||||
>
|
||||
<template
|
||||
v-if="
|
||||
col.name === 'item' ||
|
||||
col.name === 'import'
|
||||
"
|
||||
>
|
||||
{{ col.label + ': ' + col.value }}
|
||||
</template>
|
||||
</component>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCard>
|
||||
</div>
|
||||
</template>
|
||||
</QTable>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
<QPageSticky :offset="[20, 20]">
|
||||
<QBtn fab icon="upload" color="primary" @click="importBuys()" />
|
||||
<QTooltip class="text-no-wrap">
|
||||
{{ t('Import buys') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.separation-row {
|
||||
background-color: var(--vn-gray) !important;
|
||||
}
|
||||
.grid-style-transition {
|
||||
transition: transform 0.28s, background-color 0.28s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Import buys: Importar compras
|
||||
Buy deleted: Compra eliminada
|
||||
Buys deleted: Compras eliminadas
|
||||
Confirm deletion: Confirmar eliminación
|
||||
Are you sure you want to delete this buy?: Seguro que quieres eliminar esta compra?
|
||||
Are you sure you want to delete this buys?: Seguro que quieres eliminar estas compras?
|
||||
</i18n>
|
|
@ -0,0 +1,282 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { toCurrency } from 'filters/index';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const importData = ref({
|
||||
file: null,
|
||||
invoice: null,
|
||||
buys: [],
|
||||
observation: null,
|
||||
ref: null,
|
||||
});
|
||||
|
||||
const lastItemBuysOptions = ref([]);
|
||||
const packagingsOptions = ref([]);
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
label: t('entry.buys.item'),
|
||||
name: 'item',
|
||||
field: 'itemFk',
|
||||
options: lastItemBuysOptions.value,
|
||||
optionValue: 'id',
|
||||
optionLabel: 'name',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.description'),
|
||||
name: 'description',
|
||||
field: 'description',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.size'),
|
||||
name: 'size',
|
||||
field: 'size',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.packing'),
|
||||
name: 'packing',
|
||||
field: 'packing',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.grouping'),
|
||||
name: 'grouping',
|
||||
field: 'grouping',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.buyingValue'),
|
||||
name: 'buyingValue',
|
||||
field: 'buyingValue',
|
||||
align: 'left',
|
||||
format: (val) => toCurrency(val),
|
||||
},
|
||||
{
|
||||
label: t('entry.buys.packagingFk'),
|
||||
name: 'packagingFk',
|
||||
field: 'packagingFk',
|
||||
options: packagingsOptions.value,
|
||||
optionValue: 'id',
|
||||
optionLabel: 'id',
|
||||
align: 'left',
|
||||
},
|
||||
]);
|
||||
|
||||
const onFileChange = (e) => {
|
||||
importData.value.file = e;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => fillData(e.target.result);
|
||||
reader.readAsText(importData.value.file);
|
||||
};
|
||||
|
||||
const fillData = async (rawData) => {
|
||||
const data = JSON.parse(rawData);
|
||||
const [invoice] = data.invoices;
|
||||
|
||||
importData.value.observation = invoice.tx_awb;
|
||||
const companyName = invoice.tx_company;
|
||||
const boxes = invoice.boxes;
|
||||
const buys = [];
|
||||
|
||||
for (let box of boxes) {
|
||||
const boxVolume = box.nu_length * box.nu_width * box.nu_height;
|
||||
for (let product of box.products) {
|
||||
const packing = product.nu_stems_bunch * product.nu_bunches;
|
||||
buys.push({
|
||||
description: product.nm_product,
|
||||
companyName: companyName,
|
||||
size: product.nu_length,
|
||||
packing: packing,
|
||||
grouping: product.nu_stems_bunch,
|
||||
buyingValue: parseFloat(product.mny_rate_stem),
|
||||
volume: boxVolume,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const boxesId = boxes.map((box) => box.id_box);
|
||||
importData.value.ref = boxesId.join(', ');
|
||||
await fetchBuys(buys);
|
||||
};
|
||||
|
||||
const fetchBuys = async (buys) => {
|
||||
try {
|
||||
const params = { buys };
|
||||
const { data } = await axios.post(
|
||||
`Entries/${route.params.id}/importBuysPreview`,
|
||||
params
|
||||
);
|
||||
importData.value.buys = data;
|
||||
} catch (err) {
|
||||
console.error('Error fetching buys');
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
const params = importData.value;
|
||||
const hasAnyEmptyRow = params.buys.some((buy) => {
|
||||
return buy.itemFk === null;
|
||||
});
|
||||
|
||||
if (hasAnyEmptyRow) {
|
||||
notify(t('Some of the imported buys does not have an item'), 'negative');
|
||||
return;
|
||||
}
|
||||
|
||||
await axios.post(`Entries/${route.params.id}/importBuys`, params);
|
||||
notify('globals.dataSaved', 'positive');
|
||||
redirectToBuysView();
|
||||
} catch (err) {
|
||||
console.error('Error importing buys', err);
|
||||
}
|
||||
};
|
||||
|
||||
const redirectToBuysView = () => {
|
||||
router.push({ name: 'EntryBuys' });
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
:url="`Entries/${route.params.id}/lastItemBuys`"
|
||||
:filter="{ fields: ['id', 'name'] }"
|
||||
order="id DESC"
|
||||
@on-fetch="(data) => (lastItemBuysOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Packagings"
|
||||
:filter="{ fields: ['id'], where: { isBox: true } }"
|
||||
order="id ASC"
|
||||
@on-fetch="(data) => (packagingsOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<QForm>
|
||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||
<div>
|
||||
<QBtnGroup push class="q-gutter-x-sm">
|
||||
<QBtn
|
||||
:label="t('globals.cancel')"
|
||||
color="primary"
|
||||
icon="restart_alt"
|
||||
flat
|
||||
@click="redirectToBuysView()"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.save')"
|
||||
color="primary"
|
||||
icon="save"
|
||||
type="submit"
|
||||
:disable="!importData.file"
|
||||
@click="onSubmit()"
|
||||
/>
|
||||
</QBtnGroup>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QCard class="q-pa-lg">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QFile
|
||||
:label="t('entry.buys.file')"
|
||||
:multiple="false"
|
||||
v-model="importData.file"
|
||||
@update:model-value="onFileChange($event)"
|
||||
class="required"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="vn:attach" class="cursor-pointer">
|
||||
<QTooltip>{{ t('Select a file') }}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QFile>
|
||||
</div>
|
||||
</VnRow>
|
||||
<div v-if="importData.file">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('entry.buys.reference')"
|
||||
v-model="importData.ref"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('entry.buys.observations')"
|
||||
v-model="importData.observation"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QTable
|
||||
:columns="columns"
|
||||
:rows="importData.buys"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
hide-pagination
|
||||
>
|
||||
<template #body-cell-item="{ row, col }">
|
||||
<QTd auto-width>
|
||||
<VnSelectFilter
|
||||
v-model="row[col.field]"
|
||||
:options="col.options"
|
||||
:option-value="col.optionValue"
|
||||
:option-label="col.optionLabel"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.id }} -
|
||||
{{ scope.opt?.name }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-packagingFk="{ row, col }">
|
||||
<QTd auto-width>
|
||||
<VnSelectFilter
|
||||
v-model="row[col.field]"
|
||||
:options="col.options"
|
||||
:option-value="col.optionValue"
|
||||
:option-label="col.optionLabel"
|
||||
hide-selected
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
</VnRow>
|
||||
</div>
|
||||
</QCard>
|
||||
</QForm>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Select a file: Selecciona un fichero
|
||||
Some of the imported buys does not have an item: Algunas de las compras importadas no tienen un artículo
|
||||
</i18n>
|
|
@ -1,15 +1,29 @@
|
|||
<script setup>
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import EntryDescriptor from './EntryDescriptor.vue';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
</script>
|
||||
<template>
|
||||
<!-- Entry searchbar -->
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="EntryList"
|
||||
:label="t('Search entries')"
|
||||
:info="t('You can search by entry reference')"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<QScrollArea class="fit">
|
||||
<!-- EntryDescriptor -->
|
||||
<EntryDescriptor />
|
||||
<QSeparator />
|
||||
<LeftMenu source="card" />
|
||||
</QScrollArea>
|
||||
|
@ -22,3 +36,9 @@ const stateStore = useStateStore();
|
|||
</QPage>
|
||||
</QPageContainer>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search entries: Buscar entradas
|
||||
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
summary: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { openReport } = usePrintService();
|
||||
|
||||
const entryFilter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'travel',
|
||||
scope: {
|
||||
fields: ['id', 'landed', 'agencyModeFk', 'warehouseOutFk'],
|
||||
include: [
|
||||
{
|
||||
relation: 'agency',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'warehouseOut',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'warehouseIn',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'supplier',
|
||||
scope: {
|
||||
fields: ['id', 'nickname'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const entityId = computed(() => {
|
||||
return $props.id || route.params.id;
|
||||
});
|
||||
|
||||
const data = ref(useCardDescription());
|
||||
const setData = (entity) =>
|
||||
(data.value = useCardDescription(entity.supplier.nickname, entity.id));
|
||||
|
||||
const getEntryRedirectionFilter = (entry) => {
|
||||
let entryTravel = entry && entry.travel;
|
||||
|
||||
if (!entryTravel || !entryTravel.landed) return null;
|
||||
|
||||
const date = new Date(entryTravel.landed);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
const from = new Date(date.getTime());
|
||||
from.setDate(from.getDate() - 10);
|
||||
|
||||
const to = new Date(date.getTime());
|
||||
to.setDate(to.getDate() + 10);
|
||||
|
||||
return JSON.stringify({
|
||||
supplierFk: entry.supplierFk,
|
||||
from,
|
||||
to,
|
||||
});
|
||||
};
|
||||
|
||||
const showEntryReport = () => {
|
||||
openReport(`Entries/${route.params.id}/entry-order-pdf`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardDescriptor
|
||||
module="Entry"
|
||||
:url="`Entries/${entityId}`"
|
||||
:filter="entryFilter"
|
||||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
@on-fetch="setData"
|
||||
data-key="entryData"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
<QItem v-ripple clickable @click="showEntryReport(entity)">
|
||||
<QItemSection>{{ t('Show entry report') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-ripple clickable>
|
||||
<QItemSection>
|
||||
<RouterLink :to="{ name: 'EntryList' }" class="color-vn-text">
|
||||
{{ t('Go to module index') }}
|
||||
</RouterLink>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv
|
||||
:label="t('entry.descriptor.agency')"
|
||||
:value="entity.travel.agency.name"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.descriptor.landed')"
|
||||
:value="toDate(entity.travel.landed)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.descriptor.warehouseOut')"
|
||||
:value="entity.travel.warehouseOut.name"
|
||||
/>
|
||||
</template>
|
||||
<template #icons="{ entity }">
|
||||
<QCardActions class="q-gutter-x-md">
|
||||
<QIcon
|
||||
v-if="entity.isExcludedFromAvailable"
|
||||
name="vn:inventory"
|
||||
color="primary"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon v-if="entity.isRaid" name="vn:web" color="primary" size="xs">
|
||||
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
||||
</QIcon>
|
||||
</QCardActions>
|
||||
</template>
|
||||
<template #actions="{ entity }">
|
||||
<QCardActions>
|
||||
<QBtn
|
||||
:to="`/supplier/${entity.supplier.id}`"
|
||||
size="md"
|
||||
icon="vn:supplier"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('Supplier card') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:to="{
|
||||
name: 'TravelMain',
|
||||
query: {
|
||||
params: JSON.stringify({
|
||||
agencyModeFk: entity.travel?.agencyModeFk,
|
||||
}),
|
||||
},
|
||||
}"
|
||||
size="md"
|
||||
icon="local_airport"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('All travels with current agency') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:to="{
|
||||
name: 'EntryMain',
|
||||
query: {
|
||||
params: getEntryRedirectionFilter(entity),
|
||||
},
|
||||
}"
|
||||
size="md"
|
||||
icon="vn:entry"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('All entries with current supplier') }}</QTooltip>
|
||||
</QBtn>
|
||||
</QCardActions>
|
||||
</template>
|
||||
</CardDescriptor>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Supplier card: Ficha del proveedor
|
||||
All travels with current agency: Todos los envíos con la agencia actual
|
||||
All entries with current supplier: Todas las entradas con el proveedor actual
|
||||
Show entry report: Ver informe del pedido
|
||||
Go to module index: Ir al índice del modulo
|
||||
Inventory entry: Es inventario
|
||||
Virtual entry: Es una redada
|
||||
</i18n>
|
|
@ -0,0 +1,16 @@
|
|||
<script setup>
|
||||
import EntryDescriptor from './EntryDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPopupProxy>
|
||||
<EntryDescriptor v-if="$props.id" :id="$props.id" />
|
||||
</QPopupProxy>
|
||||
</template>
|
|
@ -0,0 +1,6 @@
|
|||
<script setup>
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
</script>
|
||||
<template>
|
||||
<VnLog model="Entry" url="/EntryLogs"></VnLog>
|
||||
</template>
|
|
@ -0,0 +1,99 @@
|
|||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import CrudModel from 'components/CrudModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const entryObservationsRef = ref(null);
|
||||
const entryObservationsOptions = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
if (entryObservationsRef.value) entryObservationsRef.value.reload();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="ObservationTypes"
|
||||
@on-fetch="(data) => (entryObservationsOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<CrudModel
|
||||
data-key="EntryAccount"
|
||||
url="EntryObservations"
|
||||
model="EntryAccount"
|
||||
:filter="{
|
||||
fields: ['id', 'entryFk', 'observationTypeFk', 'description'],
|
||||
where: { entryFk: route.params.id },
|
||||
}"
|
||||
ref="entryObservationsRef"
|
||||
:default-remove="false"
|
||||
:data-required="{ entryFk: route.params.id }"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QCard class="q-pa-md">
|
||||
<VnRow
|
||||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
class="row q-gutter-md q-mb-md"
|
||||
>
|
||||
<div class="col-3">
|
||||
<VnSelectFilter
|
||||
:label="t('entry.notes.observationType')"
|
||||
v-model="row.observationTypeFk"
|
||||
:options="entryObservationsOptions"
|
||||
option-label="description"
|
||||
option-value="id"
|
||||
hide-selected
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('entry.notes.description')"
|
||||
v-model="row.description"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-1 row justify-center items-center">
|
||||
<QIcon
|
||||
name="delete"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="entryObservationsRef.remove([row])"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Remove note') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QIcon
|
||||
name="add"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="entryObservationsRef.insert()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Add note') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
</template>
|
||||
</CrudModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Add note: Añadir nota
|
||||
Remove note: Quitar nota
|
||||
</i18n>
|
|
@ -0,0 +1,359 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, computed, onUpdated } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CardSummary from 'components/ui/CardSummary.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
||||
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||
|
||||
import { toDate, toCurrency } from 'src/filters';
|
||||
import { getUrl } from 'src/composables/getUrl';
|
||||
import axios from 'axios';
|
||||
|
||||
onUpdated(() => summaryRef.value.fetch());
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
const summaryRef = ref();
|
||||
const entry = ref();
|
||||
const entryBuys = ref([]);
|
||||
const entryUrl = ref();
|
||||
|
||||
onMounted(async () => {
|
||||
entryUrl.value = (await getUrl('entry/')) + entityId.value;
|
||||
});
|
||||
|
||||
const tableColumnComponents = {
|
||||
quantity: {
|
||||
component: () => 'span',
|
||||
},
|
||||
stickers: {
|
||||
component: () => 'span',
|
||||
},
|
||||
packagingFk: {
|
||||
component: () => 'span',
|
||||
},
|
||||
weight: {
|
||||
component: () => 'span',
|
||||
},
|
||||
packing: {
|
||||
component: () => 'span',
|
||||
},
|
||||
grouping: {
|
||||
component: () => 'span',
|
||||
},
|
||||
buyingValue: {
|
||||
component: () => 'span',
|
||||
},
|
||||
amount: {
|
||||
component: () => 'span',
|
||||
},
|
||||
pvp: {
|
||||
component: () => 'span',
|
||||
},
|
||||
};
|
||||
|
||||
const entriesTableColumns = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: t('entry.summary.quantity'),
|
||||
field: 'quantity',
|
||||
name: 'quantity',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.stickers'),
|
||||
field: 'stickers',
|
||||
name: 'stickers',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.package'),
|
||||
field: 'packagingFk',
|
||||
name: 'packagingFk',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.weight'),
|
||||
field: 'weight',
|
||||
name: 'weight',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.packing'),
|
||||
field: 'packing',
|
||||
name: 'packing',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.grouping'),
|
||||
field: 'grouping',
|
||||
name: 'grouping',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.buyingValue'),
|
||||
field: 'buyingValue',
|
||||
name: 'buyingValue',
|
||||
align: 'left',
|
||||
format: (value) => toCurrency(value),
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.import'),
|
||||
name: 'amount',
|
||||
align: 'left',
|
||||
format: (_, row) => toCurrency(row.buyingValue * row.quantity),
|
||||
},
|
||||
{
|
||||
label: t('entry.summary.pvp'),
|
||||
name: 'pvp',
|
||||
align: 'left',
|
||||
format: (_, row) => toCurrency(row.price2) + ' / ' + toCurrency(row.price3),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
async function setEntryData(data) {
|
||||
if (data) entry.value = data;
|
||||
await fetchEntryBuys();
|
||||
}
|
||||
|
||||
const fetchEntryBuys = async () => {
|
||||
try {
|
||||
const { data } = await axios.get(`Entries/${entry.value.id}/getBuys`);
|
||||
if (data) entryBuys.value = data;
|
||||
} catch (err) {
|
||||
console.error('Error fetching entry buys');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardSummary
|
||||
ref="summaryRef"
|
||||
:url="`Entries/${entityId}/getEntry`"
|
||||
@on-fetch="(data) => setEntryData(data)"
|
||||
>
|
||||
<template #header-left>
|
||||
<a class="header link" :href="entryUrl">
|
||||
<QIcon name="open_in_new" color="white" size="sm" />
|
||||
</a>
|
||||
</template>
|
||||
<template #header>
|
||||
<span>{{ entry.id }} - {{ entry.supplier.nickname }}</span>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<QCard class="vn-one">
|
||||
<a class="header link" :href="entryUrl">
|
||||
{{ t('globals.summary.basicData') }}
|
||||
<QIcon name="open_in_new" color="primary" />
|
||||
</a>
|
||||
<VnRow>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.commission')"
|
||||
:value="entry.commission"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.currency')"
|
||||
:value="entry.currency.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.company')"
|
||||
:value="entry.company.code"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.reference')"
|
||||
:value="entry.reference"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.invoiceNumber')"
|
||||
:value="entry.invoiceNumber"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.ordered')"
|
||||
:value="entry.isOrdered"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.confirmed')"
|
||||
:value="entry.isConfirmed"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.booked')"
|
||||
:value="entry.isBooked"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv :label="t('entry.summary.raid')" :value="entry.isRaid" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.excludedFromAvailable')"
|
||||
:value="entry.isExcludedFromAvailable"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<a class="header link" :href="entryUrl">
|
||||
{{ t('Travel data') }}
|
||||
<QIcon name="open_in_new" color="primary" />
|
||||
</a>
|
||||
<VnRow>
|
||||
<div class="col">
|
||||
<VnLv :label="t('entry.summary.travelReference')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ entry.travel.ref }}
|
||||
<TravelDescriptorProxy :id="entry.travel.id" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelAgency')"
|
||||
:value="entry.travel.agency.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelShipped')"
|
||||
:value="toDate(entry.travel.shipped)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelWarehouseOut')"
|
||||
:value="entry.travel.warehouseOut.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelDelivered')"
|
||||
:value="entry.travel.isDelivered"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelLanded')"
|
||||
:value="toDate(entry.travel.landed)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelWarehouseIn')"
|
||||
:value="entry.travel.warehouseIn.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelReceived')"
|
||||
:value="entry.travel.isReceived"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
<QCard class="vn-two" style="min-width: 100%">
|
||||
<a class="header">
|
||||
{{ t('entry.summary.buys') }}
|
||||
</a>
|
||||
<QTable
|
||||
:rows="entryBuys"
|
||||
:columns="entriesTableColumns"
|
||||
hide-bottom
|
||||
row-key="index"
|
||||
class="full-width q-mt-md"
|
||||
>
|
||||
<template #body="{ cols, row, rowIndex }">
|
||||
<QTr no-hover>
|
||||
<QTd v-for="col in cols" :key="col.name">
|
||||
<component
|
||||
:is="tableColumnComponents[col.name].component()"
|
||||
>
|
||||
<template
|
||||
v-if="
|
||||
col.name !== 'observation' &&
|
||||
col.name !== 'isConfirmed'
|
||||
"
|
||||
>{{ col.value }}</template
|
||||
>
|
||||
<QTooltip v-if="col.toolTip">{{
|
||||
col.toolTip
|
||||
}}</QTooltip>
|
||||
</component>
|
||||
</QTd>
|
||||
</QTr>
|
||||
<QTr no-hover>
|
||||
<QTd>
|
||||
<span>{{ row.item.itemType.code }}</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span>{{ row.item.id }}</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span>{{ row.item.size }}</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span>{{ toCurrency(row.item.minPrice) }}</span>
|
||||
</QTd>
|
||||
<QTd colspan="6">
|
||||
<span>{{ row.item.concept }}</span>
|
||||
<span v-if="row.item.subName" class="subName">
|
||||
{{ row.item.subName }}
|
||||
</span>
|
||||
<fetched-tags :item="row.item" :max-length="5" />
|
||||
</QTd>
|
||||
</QTr>
|
||||
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
||||
<QTr
|
||||
v-if="rowIndex !== entryBuys.length - 1"
|
||||
class="separation-row"
|
||||
>
|
||||
<QTd colspan="10" style="height: 24px" />
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QCard>
|
||||
</template>
|
||||
</CardSummary>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.separation-row {
|
||||
background-color: var(--vn-gray) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Travel data: 'Datos envío'
|
||||
</i18n>
|
|
@ -0,0 +1,29 @@
|
|||
<script setup>
|
||||
import { useDialogPluginComponent } from 'quasar';
|
||||
import EntrySummary from './EntrySummary.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits([...useDialogPluginComponent.emits]);
|
||||
|
||||
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||
<EntrySummary v-if="$props.id" :id="$props.id" />
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.q-dialog .summary .header {
|
||||
position: sticky;
|
||||
z-index: $z-max;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,246 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const currenciesOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
const suppliersOptions = ref([]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
ref="companiesRef"
|
||||
url="Companies"
|
||||
:filter="{ fields: ['id', 'code'] }"
|
||||
order="code"
|
||||
@on-fetch="(data) => (companiesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="currenciesRef"
|
||||
url="Currencies"
|
||||
:filter="{ fields: ['id', 'name'] }"
|
||||
order="code"
|
||||
@on-fetch="(data) => (currenciesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname', 'name'] }"
|
||||
order="nickname"
|
||||
@on-fetch="(data) => (suppliersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params }">
|
||||
<QList dense class="list q-gutter-y-sm q-mt-sm">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.search"
|
||||
:label="t('params.search')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.reference"
|
||||
:label="t('params.reference')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.invoiceNumber"
|
||||
:label="t('params.invoiceNumber')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.travelFk"
|
||||
:label="t('params.travelFk')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelectFilter
|
||||
:label="t('params.companyFk')"
|
||||
v-model="params.companyFk"
|
||||
:options="companiesOptions"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelectFilter
|
||||
:label="t('params.currencyFk')"
|
||||
v-model="params.currencyFk"
|
||||
:options="currenciesOptions"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelectFilter
|
||||
:label="t('params.supplierFk')"
|
||||
v-model="params.supplierFk"
|
||||
:options="suppliersOptions"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
scope.opt?.name + ': ' + scope.opt?.nickname
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('params.created')"
|
||||
is-outlined
|
||||
v-model="params.created"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('params.from')"
|
||||
is-outlined
|
||||
v-model="params.from"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('params.to')"
|
||||
is-outlined
|
||||
v-model="params.to"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QCheckbox
|
||||
:label="t('params.isBooked')"
|
||||
v-model="params.isBooked"
|
||||
toggle-indeterminate
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QCheckbox
|
||||
:label="t('params.isConfirmed')"
|
||||
v-model="params.isConfirmed"
|
||||
toggle-indeterminate
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QCheckbox
|
||||
:label="t('params.isOrdered')"
|
||||
v-model="params.isOrdered"
|
||||
toggle-indeterminate
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.list {
|
||||
width: 256px;
|
||||
}
|
||||
.list * {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
search: General search
|
||||
reference: Reference
|
||||
invoiceNumber: Invoice number
|
||||
travelFk: Travel
|
||||
companyFk: Company
|
||||
currencyFk: Currency
|
||||
supplierFk: Supplier
|
||||
from: From
|
||||
to: To
|
||||
created: Created
|
||||
isBooked: Booked
|
||||
isConfirmed: Confirmed
|
||||
isOrdered: Ordered
|
||||
es:
|
||||
params:
|
||||
search: Búsqueda general
|
||||
reference: Referencia
|
||||
invoiceNumber: Núm. factura
|
||||
travelFk: Envío
|
||||
companyFk: Empresa
|
||||
currencyFk: Moneda
|
||||
supplierFk: Proveedor
|
||||
from: Desde
|
||||
to: Hasta
|
||||
created: Fecha creación
|
||||
isBooked: Asentado
|
||||
isConfirmed: Confirmado
|
||||
isOrdered: Pedida
|
||||
</i18n>
|
|
@ -1,22 +1,144 @@
|
|||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import EntrySummaryDialog from './Card/EntrySummaryDialog.vue';
|
||||
import EntryFilter from './EntryFilter.vue';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { toDate } from 'src/filters/index';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
function navigate(id) {
|
||||
router.push({ path: `/entry/${id}` });
|
||||
}
|
||||
|
||||
const redirectToCreateView = () => {
|
||||
router.push({ name: 'EntryCreate' });
|
||||
};
|
||||
|
||||
function viewSummary(id) {
|
||||
quasar.dialog({
|
||||
component: EntrySummaryDialog,
|
||||
componentProps: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
stateStore.rightDrawer = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<EntryFilter data-key="EntryList" />
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<VnPaginate
|
||||
data-key="EntryList"
|
||||
url="Entries/filter"
|
||||
order="landed DESC, id DESC"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<CardList
|
||||
v-for="row of rows"
|
||||
:key="row.id"
|
||||
:title="row.reference"
|
||||
@click="navigate(row.id)"
|
||||
:id="row.id"
|
||||
:has-info-icons="!!row.isExcludedFromAvailable || !!row.isRaid"
|
||||
>
|
||||
<template #info-icons>
|
||||
<QIcon
|
||||
v-if="row.isExcludedFromAvailable"
|
||||
name="vn:inventory"
|
||||
color="primary"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.isRaid"
|
||||
name="vn:web"
|
||||
color="primary"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
<template #list-items>
|
||||
<VnLv
|
||||
:label="t('entry.list.landed')"
|
||||
:value="toDate(row.landed)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.list.booked')"
|
||||
:value="!!row.isBooked"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.list.invoiceNumber')"
|
||||
:value="row.invoiceNumber"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.list.confirmed')"
|
||||
:value="!!row.isConfirmed"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.list.supplier')"
|
||||
:value="row.supplierName"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.list.ordered')"
|
||||
:value="!!row.isOrdered"
|
||||
/>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
:label="t('components.smartCard.openSummary')"
|
||||
@click.stop="viewSummary(row.id)"
|
||||
color="primary"
|
||||
type="submit"
|
||||
/>
|
||||
</template>
|
||||
</CardList>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</div>
|
||||
</QPage>
|
||||
<QPageSticky :offset="[20, 20]">
|
||||
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||
<QTooltip>
|
||||
{{ t('entry.list.newEntry') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search entries: Buscar entradas
|
||||
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||
Inventory entry: Es inventario
|
||||
Virtual entry: Es una redada
|
||||
</i18n>
|
||||
|
|
|
@ -1,11 +1,24 @@
|
|||
<script setup>
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import LeftMenu from 'src/components/LeftMenu.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="EntryList"
|
||||
:label="t('Search entries')"
|
||||
:info="t('You can search by entry reference')"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<LeftMenu />
|
||||
|
@ -15,3 +28,9 @@ const stateStore = useStateStore();
|
|||
<RouterView></RouterView>
|
||||
</QPageContainer>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search entries: Buscar entradas
|
||||
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<script setup>
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||
// import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
</script>
|
||||
<template>
|
||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<QScrollArea class="fit">
|
||||
<RouteDescriptor />
|
||||
<QSeparator />
|
||||
<LeftMenu source="card" />
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<QPageContainer>
|
||||
<QPage>
|
||||
<RouterView></RouterView>
|
||||
</QPage>
|
||||
</QPageContainer>
|
||||
</template>
|
|
@ -0,0 +1,104 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import VnLv from 'components/ui/VnLv.vue';
|
||||
import useCardDescription from 'composables/useCardDescription';
|
||||
import { dashIfEmpty, toDate } from 'src/filters';
|
||||
import RouteDescriptorMenu from 'pages/Route/Card/RouteDescriptorMenu.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const entityId = computed(() => {
|
||||
return $props.id || route.params.id;
|
||||
});
|
||||
|
||||
const filter = {
|
||||
fields: [
|
||||
'id',
|
||||
'workerFk',
|
||||
'agencyModeFk',
|
||||
'created',
|
||||
'm3',
|
||||
'warehouseFk',
|
||||
'description',
|
||||
'vehicleFk',
|
||||
'kmStart',
|
||||
'kmEnd',
|
||||
'started',
|
||||
'finished',
|
||||
'cost',
|
||||
'zoneFk',
|
||||
'isOk',
|
||||
],
|
||||
include: [
|
||||
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
|
||||
{
|
||||
relation: 'vehicle',
|
||||
scope: { fields: ['id', 'm3'] },
|
||||
},
|
||||
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||
{
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: { relation: 'emailUser', scope: { fields: ['email'] } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const data = ref(useCardDescription());
|
||||
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardDescriptor
|
||||
module="Route"
|
||||
:url="`Routes/${entityId}`"
|
||||
:filter="filter"
|
||||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
data-key="Routes"
|
||||
@on-fetch="setData"
|
||||
>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('Date')" :value="toDate(entity?.created)" />
|
||||
<VnLv :label="t('Agency')" :value="entity?.agencyMode?.name" />
|
||||
<VnLv :label="t('Zone')" :value="entity?.zone?.name" />
|
||||
<VnLv
|
||||
:label="t('Volume')"
|
||||
:value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty(
|
||||
entity?.vehicle?.m3
|
||||
)} m³`"
|
||||
/>
|
||||
<VnLv :label="t('Description')" :value="entity?.description" />
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<RouteDescriptorMenu :route="entity" />
|
||||
</template>
|
||||
</CardDescriptor>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Date: Fecha
|
||||
Agency: Agencia
|
||||
Zone: Zona
|
||||
Volume: Volumen
|
||||
Description: Descripción
|
||||
</i18n>
|
|
@ -0,0 +1,63 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
|
||||
const props = defineProps({
|
||||
route: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
function confirmRemove() {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('confirmDeletion'),
|
||||
message: t('confirmDeletionMessage'),
|
||||
promise: remove,
|
||||
},
|
||||
})
|
||||
.onOk(async () => await router.push({ name: 'RouteList' }));
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
if (!props.route.id) {
|
||||
return;
|
||||
}
|
||||
await axios.delete(`Routes/${props.route.id}`);
|
||||
quasar.notify({
|
||||
message: t('globals.dataDeleted'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Add reports
|
||||
</script>
|
||||
<template>
|
||||
<QItem @click="confirmRemove" v-ripple clickable>
|
||||
<QItemSection avatar>
|
||||
<QIcon name="delete" />
|
||||
</QItemSection>
|
||||
<QItemSection>{{ t('deleteRoute') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
confirmDeletion: Confirm deletion
|
||||
confirmDeletionMessage: Are you sure you want to delete this route?
|
||||
deleteRoute: Delete route
|
||||
es:
|
||||
confirmDeletion: Confirmar eliminación,
|
||||
confirmDeletionMessage: Seguro que quieres eliminar esta ruta?
|
||||
deleteRoute: Eliminar ruta
|
||||
</i18n>
|
|
@ -0,0 +1,15 @@
|
|||
<script setup>
|
||||
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<QPopupProxy>
|
||||
<RouteDescriptor v-if="$props.id" :id="$props.id" />
|
||||
</QPopupProxy>
|
||||
</template>
|
|
@ -0,0 +1,234 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnInput from 'components/common/VnInput.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
const workerList = ref([]);
|
||||
const agencyList = ref([]);
|
||||
const vehicleList = ref([]);
|
||||
const warehouseList = ref([]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Workers/search"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
sort-by="nickname ASC"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (workerList = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="AgencyModes/isActive"
|
||||
:filter="{ fields: ['id', 'name'] }"
|
||||
sort-by="name ASC"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (agencyList = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Vehicles"
|
||||
:filter="{ fields: ['id', 'numberPlate'] }"
|
||||
sort-by="numberPlate ASC"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (vehicleList = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData url="Warehouses" @on-fetch="(data) => (warehouseList = data)" auto-load />
|
||||
<VnFilterPanel
|
||||
:data-key="props.dataKey"
|
||||
:search-button="true"
|
||||
@search="emit('search')"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params }">
|
||||
<QList dense>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection v-if="workerList">
|
||||
<VnSelectFilter
|
||||
:label="t('Worker')"
|
||||
v-model="params.workerFk"
|
||||
:options="workerList"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
>
|
||||
<template #option="{ itemProps, opt }">
|
||||
<QItem v-bind="itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ opt.nickname }},{{ opt.code }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection v-if="agencyList">
|
||||
<VnSelectFilter
|
||||
:label="t('Agency')"
|
||||
v-model="params.agencyModeFk"
|
||||
:options="agencyList"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.from"
|
||||
:label="t('From')"
|
||||
is-outlined
|
||||
:disable="Boolean(params.scopeDays)"
|
||||
@update:model-value="params.scopeDays = null"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.to"
|
||||
:label="t('To')"
|
||||
is-outlined
|
||||
:disable="Boolean(params.scopeDays)"
|
||||
@update:model-value="params.scopeDays = null"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.scopeDays"
|
||||
type="number"
|
||||
:label="t('Days Onward')"
|
||||
is-outlined
|
||||
clearable
|
||||
:disable="Boolean(params.from || params.to)"
|
||||
@update:model-value="
|
||||
params.to = null;
|
||||
params.from = null;
|
||||
"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection v-if="vehicleList">
|
||||
<VnSelectFilter
|
||||
:label="t('Vehicle')"
|
||||
v-model="params.vehicleFk"
|
||||
:options="vehicleList"
|
||||
option-value="id"
|
||||
option-label="numberPlate"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInput v-model="params.m3" label="m³" is-outlined clearable />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection v-if="vehicleList">
|
||||
<VnSelectFilter
|
||||
:label="t('Warehouse')"
|
||||
v-model="params.warehouseFk"
|
||||
:options="warehouseList"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.description"
|
||||
:label="t('Description')"
|
||||
is-outlined
|
||||
clearable
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
warehouseFk: Warehouse
|
||||
description: Description
|
||||
m3: m³
|
||||
vehicleFk: Vehicle
|
||||
agencyModeFk: Agency
|
||||
workerFk: Worker
|
||||
from: From
|
||||
to: To
|
||||
es:
|
||||
params:
|
||||
warehouseFk: Almacén
|
||||
description: Descripción
|
||||
m3: m³
|
||||
vehicleFk: Vehículo
|
||||
agencyModeFk: Agencia
|
||||
workerFk: Trabajador
|
||||
from: Desde
|
||||
to: Hasta
|
||||
Warehouse: Almacén
|
||||
Description: Descripción
|
||||
Vehicle: Vehículo
|
||||
Agency: Agencia
|
||||
Worker: Trabajador
|
||||
From: Desde
|
||||
To: Hasta
|
||||
</i18n>
|
|
@ -0,0 +1,214 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnInput from 'components/common/VnInput.vue';
|
||||
import axios from 'axios';
|
||||
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const shelvingId = route.params?.id || null;
|
||||
const isNew = Boolean(!shelvingId);
|
||||
const defaultInitialData = {
|
||||
agencyModeFk: null,
|
||||
created: null,
|
||||
description: '',
|
||||
vehicleFk: null,
|
||||
workerFk: null,
|
||||
};
|
||||
|
||||
const workerList = ref([]);
|
||||
const agencyList = ref([]);
|
||||
const vehicleList = ref([]);
|
||||
|
||||
const routeFilter = {
|
||||
fields: [
|
||||
'id',
|
||||
'workerFk',
|
||||
'agencyModeFk',
|
||||
'created',
|
||||
'm3',
|
||||
'warehouseFk',
|
||||
'description',
|
||||
'vehicleFk',
|
||||
'kmStart',
|
||||
'kmEnd',
|
||||
'started',
|
||||
'finished',
|
||||
'cost',
|
||||
'zoneFk',
|
||||
'isOk',
|
||||
],
|
||||
include: [
|
||||
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
|
||||
{
|
||||
relation: 'vehicle',
|
||||
scope: { fields: ['id', 'm3'] },
|
||||
},
|
||||
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||
{
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: { relation: 'emailUser', scope: { fields: ['email'] } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const onSave = (data, response) => {
|
||||
if (isNew) {
|
||||
axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
|
||||
router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<VnSubToolbar />
|
||||
<FetchData
|
||||
url="Workers/search"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
sort-by="nickname ASC"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (workerList = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="AgencyModes/isActive"
|
||||
:filter="{ fields: ['id', 'name'] }"
|
||||
sort-by="name ASC"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (agencyList = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Vehicles"
|
||||
:filter="{ fields: ['id', 'numberPlate'] }"
|
||||
sort-by="numberPlate ASC"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (vehicleList = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FormModel
|
||||
:url="isNew ? null : `Routes/${shelvingId}`"
|
||||
:url-create="isNew ? 'Routes' : null"
|
||||
:observe-form-changes="!isNew"
|
||||
:filter="routeFilter"
|
||||
model="route"
|
||||
:auto-load="!isNew"
|
||||
:form-initial-data="defaultInitialData"
|
||||
@on-data-saved="onSave"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Worker')"
|
||||
v-model="data.workerFk"
|
||||
:options="workerList"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
>
|
||||
<template #option="{ itemProps, opt }">
|
||||
<QItem v-bind="itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ opt.nickname }},{{ opt.code }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Vehicle')"
|
||||
v-model="data.vehicleFk"
|
||||
:options="vehicleList"
|
||||
option-value="id"
|
||||
option-label="numberPlate"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('Agency')"
|
||||
v-model="data.agencyModeFk"
|
||||
:options="agencyList"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInputDate v-model="data.created" :label="t('Created')" />
|
||||
</div>
|
||||
</VnRow>
|
||||
<template v-if="!isNew">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.kmStart"
|
||||
:label="t('Km Start')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput v-model="data.kmEnd" :label="t('Km End')" clearable />
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInputTime
|
||||
v-model="data.started"
|
||||
:label="t('Hour started')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInputTime
|
||||
v-model="data.finished"
|
||||
:label="t('Hour finished')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.description"
|
||||
:label="t('Description')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
|
@ -0,0 +1,20 @@
|
|||
<script setup>
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import {useI18n} from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnSearchbar
|
||||
data-key="RouteList"
|
||||
:label="t('Search route')"
|
||||
:info="t('You can search by route reference')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<i18n>
|
||||
es:
|
||||
Search route: Buscar rutas
|
||||
You can search by route reference: Puedes buscar por referencia de la ruta
|
||||
</i18n>
|
|
@ -0,0 +1,314 @@
|
|||
<script setup>
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import CardSummary from 'components/ui/CardSummary.vue';
|
||||
import VnLv from 'components/ui/VnLv.vue';
|
||||
import { QIcon } from 'quasar';
|
||||
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
|
||||
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import axios from 'axios';
|
||||
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const route = useRoute();
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
const isDialog = Boolean($props.id);
|
||||
const hideRightDrawer = () => {
|
||||
if (!isDialog) {
|
||||
stateStore.rightDrawer = false;
|
||||
}
|
||||
};
|
||||
onMounted(hideRightDrawer);
|
||||
onUnmounted(hideRightDrawer);
|
||||
const getTotalPackages = (tickets) => {
|
||||
return (tickets || []).reduce((sum, ticket) => sum + ticket.packages, 0);
|
||||
};
|
||||
|
||||
const ticketColumns = ref([
|
||||
{
|
||||
name: 'order',
|
||||
label: t('route.summary.order'),
|
||||
field: (row) => dashIfEmpty(row?.priority),
|
||||
sortable: false,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'street',
|
||||
label: t('route.summary.street'),
|
||||
field: (row) => row?.street,
|
||||
sortable: false,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
label: t('route.summary.city'),
|
||||
field: (row) => row?.city,
|
||||
sortable: false,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'pc',
|
||||
label: t('route.summary.pc'),
|
||||
field: (row) => row?.postalCode,
|
||||
sortable: false,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'client',
|
||||
label: t('route.summary.client'),
|
||||
field: (row) => row?.nickname,
|
||||
sortable: false,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'warehouse',
|
||||
label: t('route.summary.warehouse'),
|
||||
field: (row) => row?.warehouseName,
|
||||
sortable: false,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'packages',
|
||||
label: t('route.summary.packages'),
|
||||
field: (row) => row?.packages,
|
||||
sortable: false,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'volume',
|
||||
label: t('route.summary.m3'),
|
||||
field: (row) => row?.volume,
|
||||
sortable: false,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'packaging',
|
||||
label: t('route.summary.packaging'),
|
||||
field: (row) => row?.ipt,
|
||||
sortable: false,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'ticket',
|
||||
label: t('route.summary.ticket'),
|
||||
field: (row) => row?.id,
|
||||
sortable: false,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'observations',
|
||||
label: '',
|
||||
field: (row) => row?.ticketObservation,
|
||||
sortable: false,
|
||||
align: 'left',
|
||||
},
|
||||
]);
|
||||
|
||||
const openBuscaman = async (route, ticket) => {
|
||||
if (!route.vehicleFk) throw new Error(`The route doesn't have a vehicle`);
|
||||
const response = await axios.get(`Routes/${route.vehicleFk}/getDeliveryPoint`);
|
||||
if (!response.data)
|
||||
throw new Error(`The route's vehicle doesn't have a delivery point`);
|
||||
|
||||
const address = `${response.data}+to:${ticket.postalCode} ${ticket.city} ${ticket.street}`;
|
||||
window.open(
|
||||
'https://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=' +
|
||||
encodeURI(address),
|
||||
'_blank'
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<CardSummary ref="summary" :url="`Routes/${entityId}/summary`">
|
||||
<template #header-left>
|
||||
<RouterLink :to="{ name: `RouteSummary`, params: { id: entityId } }">
|
||||
<QIcon name="open_in_new" color="white" size="sm" />
|
||||
</RouterLink>
|
||||
</template>
|
||||
<template #header="{ entity }">
|
||||
<span>{{ `${entity?.route.id} - ${entity?.route?.description}` }}</span>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<QCard class="vn-one">
|
||||
<VnLv :label="t('ID')" :value="entity?.route.id" />
|
||||
<VnLv
|
||||
:label="t('route.summary.date')"
|
||||
:value="toDate(entity?.route.created)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.agency')"
|
||||
:value="entity?.route?.agencyMode?.name"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.vehicle')"
|
||||
:value="entity.route?.vehicle?.numberPlate"
|
||||
/>
|
||||
<VnLv :label="t('route.summary.driver')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ dashIfEmpty(entity?.route?.worker?.user?.name) }}
|
||||
<WorkerDescriptorProxy :id="entity.route?.workerFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('route.summary.cost')"
|
||||
:value="toCurrency(entity.route?.cost)"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<VnLv
|
||||
:label="t('route.summary.started')"
|
||||
:value="toHour(entity?.route.started)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.finished')"
|
||||
:value="toHour(entity?.route.finished)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.kmStart')"
|
||||
:value="dashIfEmpty(entity?.route?.kmStart)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.kmEnd')"
|
||||
:value="dashIfEmpty(entity?.route?.kmEnd)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.volume')"
|
||||
:value="`${dashIfEmpty(entity?.route?.m3)} / ${dashIfEmpty(
|
||||
entity?.route?.vehicle?.m3
|
||||
)} m³`"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('route.summary.packages')"
|
||||
:value="getTotalPackages(entity.tickets)"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<div class="header">
|
||||
{{ t('route.summary.description') }}
|
||||
</div>
|
||||
<p>
|
||||
{{ dashIfEmpty(entity?.route?.description) }}
|
||||
</p>
|
||||
</QCard>
|
||||
|
||||
<QCard class="vn-max">
|
||||
<div class="header">
|
||||
{{ t('route.summary.tickets') }}
|
||||
</div>
|
||||
<QTable
|
||||
:columns="ticketColumns"
|
||||
:rows="entity?.tickets"
|
||||
:rows-per-page-options="[0]"
|
||||
row-key="id"
|
||||
flat
|
||||
hide-pagination
|
||||
>
|
||||
<template #body-cell-city="{ value, row }">
|
||||
<QTd auto-width>
|
||||
<span
|
||||
class="text-primary cursor-pointer"
|
||||
@click="openBuscaman(entity?.route, row)"
|
||||
>
|
||||
{{ value }}
|
||||
</span>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-client="{ value, row }">
|
||||
<QTd auto-width>
|
||||
<span class="text-primary cursor-pointer">
|
||||
{{ value }}
|
||||
<CustomerDescriptorProxy :id="row?.clientFk" />
|
||||
</span>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-ticket="{ value, row }">
|
||||
<QTd auto-width>
|
||||
<span class="text-primary cursor-pointer">
|
||||
{{ value }}
|
||||
<TicketDescriptorProxy :id="row?.id" />
|
||||
</span>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-observations="{ value }">
|
||||
<QTd auto-width>
|
||||
<QIcon
|
||||
v-if="value"
|
||||
name="vn:notes"
|
||||
color="primary"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<QTooltip>{{ value }}</QTooltip>
|
||||
</QIcon>
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
</QCard>
|
||||
</template>
|
||||
</CardSummary>
|
||||
</div>
|
||||
</template>
|
||||
<i18n>
|
||||
en:
|
||||
route:
|
||||
summary:
|
||||
date: Date
|
||||
agency: Agency
|
||||
vehicle: Vehicle
|
||||
driver: Driver
|
||||
cost: Cost
|
||||
started: Started time
|
||||
finished: Finished time
|
||||
kmStart: Km start
|
||||
kmEnd: Km end
|
||||
volume: Volume
|
||||
packages: Packages
|
||||
description: Description
|
||||
tickets: Tickets
|
||||
order: Order
|
||||
street: Street
|
||||
city: City
|
||||
pc: PC
|
||||
client: Client
|
||||
warehouse: Warehouse
|
||||
m3: m³
|
||||
packaging: Packaging
|
||||
ticket: Ticket
|
||||
es:
|
||||
route:
|
||||
summary:
|
||||
date: Fecha
|
||||
agency: Agencia
|
||||
vehicle: Vehículo
|
||||
driver: Conductor
|
||||
cost: Costo
|
||||
started: Hora inicio
|
||||
finished: Hora fin
|
||||
kmStart: Km inicio
|
||||
kmEnd: Km fin
|
||||
volume: Volumen
|
||||
packages: Bultos
|
||||
description: Descripción
|
||||
tickets: Tickets
|
||||
order: Orden
|
||||
street: Dirección fiscal
|
||||
city: Población
|
||||
pc: CP
|
||||
client: Cliente
|
||||
warehouse: Almacén
|
||||
packaging: Encajado
|
||||
</i18n>
|
|
@ -0,0 +1,29 @@
|
|||
<script setup>
|
||||
import { useDialogPluginComponent } from 'quasar';
|
||||
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits([...useDialogPluginComponent.emits]);
|
||||
|
||||
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||
<RouteSummary v-if="$props.id" :id="$props.id" />
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.q-dialog .route .header {
|
||||
position: sticky;
|
||||
z-index: $z-max;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,530 @@
|
|||
<script setup>
|
||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import { useValidator } from 'composables/useValidator';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnInput from 'components/common/VnInput.vue';
|
||||
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||
import axios from 'axios';
|
||||
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
||||
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import RouteSummaryDialog from 'pages/Route/Card/RouteSummaryDialog.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const { validate } = useValidator();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const to = Date.vnNew();
|
||||
to.setDate(to.getDate() + 1);
|
||||
to.setHours(0, 0, 0, 0);
|
||||
|
||||
const from = Date.vnNew();
|
||||
from.setDate(from.getDate());
|
||||
from.setHours(0, 0, 0, 0);
|
||||
|
||||
const params = ref({ from, to });
|
||||
|
||||
onMounted(() => (stateStore.rightDrawer = true));
|
||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||
|
||||
const selectedRows = ref([]);
|
||||
const columns = computed(() => [
|
||||
{
|
||||
name: 'ID',
|
||||
label: t('ID'),
|
||||
field: (row) => row.id,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'worker',
|
||||
label: t('Worker'),
|
||||
field: (row) => row.workerUserName,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'agency',
|
||||
label: t('Agency'),
|
||||
field: (row) => row.agencyName,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'vehicle',
|
||||
label: t('Vehicle'),
|
||||
field: (row) => row.vehiclePlateNumber,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'date',
|
||||
label: t('Date'),
|
||||
field: (row) => row.created,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'volume',
|
||||
label: 'm³',
|
||||
field: (row) => dashIfEmpty(row.m3),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: t('Description'),
|
||||
field: (row) => row.description,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'started',
|
||||
label: t('Hour started'),
|
||||
field: (row) => toHour(row.started),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'finished',
|
||||
label: t('Hour finished'),
|
||||
field: (row) => toHour(row.finished),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'actions',
|
||||
label: '',
|
||||
sortable: false,
|
||||
align: 'right',
|
||||
},
|
||||
]);
|
||||
|
||||
const refreshKey = ref(0);
|
||||
const workers = ref([]);
|
||||
const agencyList = ref([]);
|
||||
const vehicleList = ref([]);
|
||||
const updateRoute = async (route) => {
|
||||
try {
|
||||
return await axios.patch(`Routes/${route.id}`, route);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
const updateVehicle = (row, vehicle) => {
|
||||
row.vehicleFk = vehicle.id;
|
||||
row.vehiclePlateNumber = vehicle.numberPlate;
|
||||
updateRoute(row);
|
||||
};
|
||||
|
||||
const updateAgency = (row, agency) => {
|
||||
row.agencyModeFk = agency.id;
|
||||
row.agencyName = agency.name;
|
||||
updateRoute(row);
|
||||
};
|
||||
|
||||
const updateWorker = (row, worker) => {
|
||||
row.workerFk = worker.id;
|
||||
row.workerUserName = worker.name;
|
||||
updateRoute(row);
|
||||
};
|
||||
|
||||
const confirmationDialog = ref(false);
|
||||
const startingDate = ref(null);
|
||||
|
||||
const cloneRoutes = () => {
|
||||
axios.post('Routes/clone', {
|
||||
created: startingDate.value,
|
||||
ids: selectedRows.value.map((row) => row?.id),
|
||||
});
|
||||
refreshKey.value++;
|
||||
startingDate.value = null;
|
||||
};
|
||||
|
||||
const markAsServed = () => {
|
||||
selectedRows.value.forEach((row) => {
|
||||
if (row?.id) {
|
||||
axios.patch(`Routes/${row?.id}`, { isOk: true });
|
||||
}
|
||||
});
|
||||
refreshKey.value++;
|
||||
startingDate.value = null;
|
||||
};
|
||||
|
||||
function previewRoute(id) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
quasar.dialog({
|
||||
component: RouteSummaryDialog,
|
||||
componentProps: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<RouteSearchbar />
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<QBtn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<QTooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<QDialog v-model="confirmationDialog">
|
||||
<QCard style="min-width: 350px">
|
||||
<QCardSection>
|
||||
<p class="text-h6 q-ma-none">{{ t('Select the starting date') }}</p>
|
||||
</QCardSection>
|
||||
|
||||
<QCardSection class="q-pt-none">
|
||||
<VnInputDate
|
||||
:label="t('Stating date')"
|
||||
v-model="startingDate"
|
||||
autofocus
|
||||
/>
|
||||
</QCardSection>
|
||||
<!-- TODO: Add report -->
|
||||
<QCardActions align="right">
|
||||
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
||||
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
||||
{{ t('Clone') }}
|
||||
</QBtn>
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<RouteFilter data-key="RouteList" />
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<FetchData
|
||||
url="Workers/activeWithInheritedRole"
|
||||
@on-fetch="(data) => (workers = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
|
||||
<FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
|
||||
<QPage class="column items-center">
|
||||
<QToolbar class="bg-vn-dark justify-end">
|
||||
<div id="st-actions" class="q-pa-sm">
|
||||
<QBtn
|
||||
icon="vn:clone"
|
||||
color="primary"
|
||||
class="q-mr-sm"
|
||||
:disable="!selectedRows?.length"
|
||||
@click="confirmationDialog = true"
|
||||
>
|
||||
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
|
||||
</QBtn>
|
||||
|
||||
<QBtn
|
||||
icon="check"
|
||||
color="primary"
|
||||
class="q-mr-sm"
|
||||
:disable="!selectedRows?.length"
|
||||
@click="markAsServed"
|
||||
>
|
||||
<QTooltip>{{ t('Mark as served') }}</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</QToolbar>
|
||||
<div class="route-list">
|
||||
<VnPaginate
|
||||
:key="refreshKey"
|
||||
data-key="RouteList"
|
||||
url="Routes/filter"
|
||||
:order="['created DESC', 'id DESC']"
|
||||
:limit="20"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<div class="q-pa-md">
|
||||
<QTable
|
||||
v-model:selected="selectedRows"
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
flat
|
||||
row-key="id"
|
||||
selection="multiple"
|
||||
:rows-per-page-options="[0]"
|
||||
hide-pagination
|
||||
>
|
||||
<template #body-cell-worker="props">
|
||||
<QTd :props="props">
|
||||
{{ props.row?.workerUserName }}
|
||||
<QPopupEdit
|
||||
:model-value="props.row.workerFk"
|
||||
v-slot="scope"
|
||||
buttons
|
||||
@update:model-value="
|
||||
(worker) => updateWorker(props.row, worker)
|
||||
"
|
||||
>
|
||||
<VnSelectFilter
|
||||
:label="t('Worker')"
|
||||
v-model="scope.value"
|
||||
:options="workers"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
autofocus
|
||||
:emit-value="false"
|
||||
:rules="validate('Route.workerFk')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
>
|
||||
<template #option="{ opt, itemProps }">
|
||||
<QItem
|
||||
v-bind="itemProps"
|
||||
class="q-pa-xs row items-center"
|
||||
>
|
||||
<QItemSection
|
||||
class="col-9 justify-center"
|
||||
>
|
||||
<span>{{ opt.name }}</span>
|
||||
<span class="text-grey">{{
|
||||
opt.nickname
|
||||
}}</span>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-agency="props">
|
||||
<QTd :props="props">
|
||||
{{ props.row?.agencyName }}
|
||||
<QPopupEdit
|
||||
:model-value="props.row.agencyModeFk"
|
||||
v-slot="scope"
|
||||
buttons
|
||||
@update:model-value="
|
||||
(agency) => updateAgency(props.row, agency)
|
||||
"
|
||||
>
|
||||
<VnSelectFilter
|
||||
:label="t('Agency')"
|
||||
v-model="scope.value"
|
||||
:options="agencyList"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
autofocus
|
||||
:emit-value="false"
|
||||
:rules="validate('route.agencyFk')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
/>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-vehicle="props">
|
||||
<QTd :props="props">
|
||||
{{ props.row?.vehiclePlateNumber }}
|
||||
<QPopupEdit
|
||||
:model-value="props.row.vehicleFk"
|
||||
v-slot="scope"
|
||||
buttons
|
||||
@update:model-value="
|
||||
(vehicle) => updateVehicle(props.row, vehicle)
|
||||
"
|
||||
>
|
||||
<VnSelectFilter
|
||||
:label="t('Vehicle')"
|
||||
v-model="scope.value"
|
||||
:options="vehicleList"
|
||||
option-value="id"
|
||||
option-label="numberPlate"
|
||||
hide-selected
|
||||
autofocus
|
||||
:emit-value="false"
|
||||
:rules="validate('route.vehicleFk')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
/>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-date="props">
|
||||
<QTd :props="props">
|
||||
{{ toDate(props.row?.created) }}
|
||||
<QPopupEdit
|
||||
v-model="props.row.created"
|
||||
v-slot="scope"
|
||||
@update:model-value="updateRoute(props.row)"
|
||||
buttons
|
||||
>
|
||||
<VnInputDate
|
||||
v-model="scope.value"
|
||||
autofocus
|
||||
:label="t('Date')"
|
||||
:rules="validate('route.created')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
/>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-description="props">
|
||||
<QTd :props="props">
|
||||
{{ props.row?.description }}
|
||||
<QPopupEdit
|
||||
v-model="props.row.description"
|
||||
v-slot="scope"
|
||||
@update:model-value="updateRoute(props.row)"
|
||||
buttons
|
||||
>
|
||||
<VnInput
|
||||
v-model="scope.value"
|
||||
autofocus
|
||||
:label="t('Description')"
|
||||
:rules="validate('route.description')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
/>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-started="props">
|
||||
<QTd :props="props">
|
||||
{{ toHour(props.row.started) }}
|
||||
<QPopupEdit
|
||||
v-model="props.row.started"
|
||||
v-slot="scope"
|
||||
buttons
|
||||
@update:model-value="updateRoute(props.row)"
|
||||
>
|
||||
<VnInputTime
|
||||
v-model="scope.value"
|
||||
autofocus
|
||||
:label="t('Hour started')"
|
||||
:rules="validate('route.started')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
/>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-finished="props">
|
||||
<QTd :props="props">
|
||||
{{ toHour(props.row.finished) }}
|
||||
<QPopupEdit
|
||||
v-model="props.row.finished"
|
||||
v-slot="scope"
|
||||
buttons
|
||||
@update:model-value="updateRoute(props.row)"
|
||||
>
|
||||
<VnInputTime
|
||||
v-model="scope.value"
|
||||
autofocus
|
||||
:label="t('Hour finished')"
|
||||
:rules="validate('route.finished')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter="scope.set"
|
||||
@focus="($event) => $event.target.select()"
|
||||
/>
|
||||
</QPopupEdit>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<QTd :props="props">
|
||||
<div class="table-actions">
|
||||
<QIcon
|
||||
name="vn:ticketAdd"
|
||||
size="xs"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('Add ticket') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
name="preview"
|
||||
size="xs"
|
||||
color="primary"
|
||||
@click="previewRoute(props?.row?.id)"
|
||||
>
|
||||
<QTooltip>{{ t('Preview') }}</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
</div>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</div>
|
||||
<QPageSticky :offset="[20, 20]">
|
||||
<RouterLink :to="{ name: 'RouteCreate' }">
|
||||
<QBtn fab icon="add" color="primary" />
|
||||
<QTooltip>
|
||||
{{ t('newRoute') }}
|
||||
</QTooltip>
|
||||
</RouterLink>
|
||||
</QPageSticky>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.route-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
newRoute: New Route
|
||||
es:
|
||||
ID: ID
|
||||
Worker: Trabajador
|
||||
Agency: Agencia
|
||||
Vehicle: Vehículo
|
||||
Date: Fecha
|
||||
Description: Descripción
|
||||
Hour started: Hora inicio
|
||||
Hour finished: Hora fin
|
||||
newRoute: Nueva Ruta
|
||||
Clone Selected Routes: Clonar rutas seleccionadas
|
||||
Select the starting date: Seleccione la fecha de inicio
|
||||
Stating date: Fecha de inicio
|
||||
Cancel: Cancelar
|
||||
Clone: Clonar
|
||||
Mark as served: Marcar como servidas
|
||||
</i18n>
|
|
@ -1 +1,177 @@
|
|||
<template>Supplier accounts</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import CrudModel from 'components/CrudModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { notify } = useNotify();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const supplier = ref(null);
|
||||
const supplierAccountRef = ref(null);
|
||||
const wireTransferFk = ref(null);
|
||||
const bankEntitiesOptions = ref([]);
|
||||
|
||||
const onBankEntityCreated = (data) => {
|
||||
bankEntitiesOptions.value.push(data);
|
||||
};
|
||||
|
||||
const onChangesSaved = () => {
|
||||
if (supplier.value.payMethodFk !== wireTransferFk.value)
|
||||
quasar
|
||||
.dialog({
|
||||
message: t('Do you want to change the pay method to wire transfer?'),
|
||||
ok: {
|
||||
push: true,
|
||||
color: 'primary',
|
||||
},
|
||||
cancel: true,
|
||||
})
|
||||
.onOk(async () => {
|
||||
await setWireTransfer();
|
||||
});
|
||||
};
|
||||
|
||||
const setWireTransfer = async () => {
|
||||
try {
|
||||
const params = {
|
||||
id: route.params.id,
|
||||
payMethodFk: wireTransferFk.value,
|
||||
};
|
||||
|
||||
await axios.patch(`Suppliers/${route.params.id}`, params);
|
||||
notify('globals.dataSaved', 'positive');
|
||||
} catch (err) {
|
||||
console.error('Error setting wire transfer', err);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (supplierAccountRef.value) supplierAccountRef.value.reload();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="BankEntities"
|
||||
@on-fetch="(data) => (bankEntitiesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="payMethods/findOne"
|
||||
@on-fetch="(data) => (wireTransferFk = data.id)"
|
||||
:filter="{ where: { code: 'wireTransfer' } }"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
:url="`Suppliers/${route.params.id}`"
|
||||
@on-fetch="(data) => (supplier = data)"
|
||||
auto-load
|
||||
/>
|
||||
<CrudModel
|
||||
data-key="SupplierAccount"
|
||||
url="SupplierAccounts"
|
||||
model="SupplierAccounts"
|
||||
:filter="{
|
||||
fields: ['id', 'supplierFk', 'iban', 'bankEntityFk', 'beneficiary'],
|
||||
where: { supplierFk: route.params.id },
|
||||
}"
|
||||
ref="supplierAccountRef"
|
||||
:default-remove="false"
|
||||
:data-required="{ supplierFk: route.params.id }"
|
||||
@save-changes="onChangesSaved()"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QCard class="q-pa-md">
|
||||
<VnRow
|
||||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
class="row q-gutter-md q-mb-md"
|
||||
>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('supplier.accounts.iban')"
|
||||
v-model="row.iban"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('worker.create.bankEntity')"
|
||||
v-model="row.bankEntityFk"
|
||||
:options="bankEntitiesOptions"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
hide-selected
|
||||
>
|
||||
<template #form>
|
||||
<CreateBankEntityForm
|
||||
@on-data-saved="onBankEntityCreated($event)"
|
||||
:show-entity-field="false"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel
|
||||
>{{ scope.opt.bic }}
|
||||
{{ scope.opt.name }}</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('supplier.accounts.beneficiary')"
|
||||
v-model="row.beneficiary"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-1 row justify-center items-center">
|
||||
<QIcon
|
||||
name="delete"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="supplierAccountRef.remove([row])"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Remove account') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QIcon
|
||||
name="add"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="supplierAccountRef.insert()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Add account') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
</template>
|
||||
</CrudModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Do you want to change the pay method to wire transfer?: ¿Quieres modificar la forma de pago a transferencia?
|
||||
Add account: Añadir cuenta
|
||||
Remove account: Remover cuenta
|
||||
</i18n>
|
||||
|
|
|
@ -1 +1,97 @@
|
|||
<template>Supplier addresses</template>
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const addressesFilter = {
|
||||
fields: [
|
||||
'id',
|
||||
'nickname',
|
||||
'street',
|
||||
'city',
|
||||
'provinceFk',
|
||||
'phone',
|
||||
'mobile',
|
||||
'postalCode',
|
||||
],
|
||||
order: ['nickname ASC'],
|
||||
include: [
|
||||
{
|
||||
relation: 'province',
|
||||
scope: {
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const redirectToCreateView = () => {
|
||||
router.push({ name: 'SupplierAddressesCreate' });
|
||||
};
|
||||
|
||||
const redirectToUpdateView = (addressData) => {
|
||||
const stringifiedAddressData = JSON.stringify(addressData);
|
||||
router.push({
|
||||
name: 'SupplierAddressesCreate',
|
||||
query: { addressData: stringifiedAddressData },
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<VnPaginate
|
||||
data-key="SupplierAddress"
|
||||
:url="`Suppliers/${route.params.id}/addresses`"
|
||||
:filter="addressesFilter"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<CardList
|
||||
v-for="row of rows"
|
||||
:key="row.id"
|
||||
:title="row.nickname"
|
||||
:id="row.id"
|
||||
@click="redirectToUpdateView(row)"
|
||||
>
|
||||
<template #list-items>
|
||||
<VnLv
|
||||
:label="t('supplier.addresses.street')"
|
||||
:value="row.street"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('supplier.addresses.postcode')"
|
||||
:value="`${row.postalCode} - ${row.city}, ${row.province.name}`"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('supplier.addresses.phone')"
|
||||
:value="`${row.phone}, ${row.mobile}`"
|
||||
/>
|
||||
</template>
|
||||
</CardList>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</div>
|
||||
<QPageSticky :offset="[20, 20]">
|
||||
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||
<QTooltip>
|
||||
{{ t('supplier.list.newSupplier') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { reactive, ref, onMounted, onBeforeMount } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const provincesOptions = ref([]);
|
||||
const postcodesOptions = ref([]);
|
||||
const townsLocationOptions = ref([]);
|
||||
const viewAction = ref();
|
||||
const updateAddressId = ref(null);
|
||||
const newAddressForm = reactive({
|
||||
nickname: null,
|
||||
street: null,
|
||||
postalCode: null,
|
||||
city: null,
|
||||
provinceFk: null,
|
||||
phone: null,
|
||||
mobile: null,
|
||||
});
|
||||
|
||||
const onDataSaved = () => {
|
||||
router.push({ name: 'SupplierAddresses' });
|
||||
};
|
||||
|
||||
const updateAddressForm = (addressData) => {
|
||||
for (let key in newAddressForm) {
|
||||
if (key in addressData) {
|
||||
newAddressForm[key] = addressData[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
viewAction.value = route.query.addressData ? 'update' : 'create';
|
||||
|
||||
if (viewAction.value === 'create') newAddressForm.supplierFk = route.params.id;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (viewAction.value === 'update' && route.query.addressData) {
|
||||
const addressData = JSON.parse(route.query.addressData);
|
||||
updateAddressId.value = addressData.id;
|
||||
updateAddressForm(addressData);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
ref="postcodeFetchDataRef"
|
||||
url="Postcodes/location"
|
||||
@on-fetch="(data) => (postcodesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="provincesFetchDataRef"
|
||||
@on-fetch="(data) => (provincesOptions = data)"
|
||||
auto-load
|
||||
url="Provinces"
|
||||
/>
|
||||
<FetchData
|
||||
ref="townsFetchDataRef"
|
||||
@on-fetch="(data) => (townsLocationOptions = data)"
|
||||
auto-load
|
||||
url="Towns/location"
|
||||
/>
|
||||
<QPage>
|
||||
<FormModel
|
||||
model="supplierAddresses"
|
||||
:form-initial-data="newAddressForm"
|
||||
:url-update="
|
||||
viewAction !== 'create' ? `SupplierAddresses/${updateAddressId}` : null
|
||||
"
|
||||
:url-create="viewAction === 'create' ? 'SupplierAddresses' : null"
|
||||
:observe-form-changes="viewAction === 'create'"
|
||||
@on-data-saved="onDataSaved()"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.nickname"
|
||||
:label="t('supplier.addresses.name')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.street"
|
||||
:label="t('supplier.addresses.street')"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
v-model="data.postalCode"
|
||||
:label="t('supplier.addresses.postcode')"
|
||||
:rules="validate('supplierAddress.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:options="postcodesOptions"
|
||||
option-label="code"
|
||||
option-value="code"
|
||||
hide-selected
|
||||
>
|
||||
<template #form>
|
||||
<CustomerCreateNewPostcode
|
||||
@on-data-saved="onPostcodeCreated($event)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||
<QItemLabel caption
|
||||
>{{ scope.opt.code }} -
|
||||
{{ scope.opt.town.name }} ({{
|
||||
scope.opt.town.province.name
|
||||
}},
|
||||
{{
|
||||
scope.opt.town.province.country.country
|
||||
}})</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.addresses.city')"
|
||||
:options="townsLocationOptions"
|
||||
v-model="data.city"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.addresses.province')"
|
||||
:options="provincesOptions"
|
||||
hide-selected
|
||||
map-options
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.provinceFk"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.phone"
|
||||
:label="t('supplier.addresses.phone')"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.mobile"
|
||||
:label="t('supplier.addresses.mobile')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col" />
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</QPage>
|
||||
</template>
|
|
@ -1 +1,139 @@
|
|||
<template>Supplier agency term</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import CrudModel from 'components/CrudModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const supplierAgencyTermRef = ref(null);
|
||||
const agenciesOptions = ref(null);
|
||||
|
||||
const supplierAgencyFilter = {
|
||||
include: {
|
||||
relation: 'agency',
|
||||
scope: {
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
where: { supplierFk: route.params.id },
|
||||
};
|
||||
|
||||
const redirectToCreateView = () => {
|
||||
router.push({ name: 'SupplierAgencyTermCreate' });
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (supplierAgencyTermRef.value) supplierAgencyTermRef.value.reload();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers/freeAgencies"
|
||||
@on-fetch="(data) => (agenciesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<CrudModel
|
||||
ref="supplierAgencyTermRef"
|
||||
data-key="SupplierAgencyTerm"
|
||||
save-url="SupplierAgencyTerms/crud"
|
||||
url="SupplierAgencyTerms"
|
||||
model="SupplierAgencyTerm"
|
||||
primary-key="agencyFk"
|
||||
:filter="supplierAgencyFilter"
|
||||
:default-remove="false"
|
||||
:data-required="{
|
||||
supplierFk: route.params.id,
|
||||
}"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QCard class="q-pa-md">
|
||||
<VnRow
|
||||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
class="row q-gutter-md q-mb-md"
|
||||
>
|
||||
<div class="col">
|
||||
<QField :label="t('supplier.agencyTerms.agencyFk')" stack-label>
|
||||
<template #control>
|
||||
<div tabindex="0">
|
||||
{{ row.agency?.name }}
|
||||
</div>
|
||||
</template>
|
||||
</QField>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.minimumM3')"
|
||||
v-model.number="row.minimumM3"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.packagePrice')"
|
||||
v-model.number="row.packagePrice"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.kmPrice')"
|
||||
v-model.number="row.kmPrice"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.m3Price')"
|
||||
v-model.number="row.m3Price"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.routePrice')"
|
||||
v-model.number="row.routePrice"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.minimumKm')"
|
||||
v-model.number="row.minimumKm"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-1 row justify-center items-center">
|
||||
<QIcon
|
||||
name="delete"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="supplierAgencyTermRef.remove([row])"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Remove row') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
</template>
|
||||
</CrudModel>
|
||||
<QPageSticky :offset="[20, 20]">
|
||||
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||
<QTooltip>
|
||||
{{ t('supplier.agencyTerms.addRow') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Remove row: Eliminar fila
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const agenciesOptions = ref(null);
|
||||
const newAgencyTermForm = reactive({
|
||||
agencyFk: null,
|
||||
minimumM3: null,
|
||||
packagePrice: null,
|
||||
kmPrice: null,
|
||||
m3Price: null,
|
||||
routePrice: null,
|
||||
minimumKm: null,
|
||||
supplierFk: route.params.id,
|
||||
});
|
||||
|
||||
const onDataSaved = () => {
|
||||
router.push({ name: 'SupplierAgencyTerm' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers/freeAgencies"
|
||||
@on-fetch="(data) => (agenciesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
|
||||
<QPage>
|
||||
<FormModel
|
||||
model="supplierAgencyTerm"
|
||||
:form-initial-data="newAgencyTermForm"
|
||||
url-create="SupplierAgencyTerms"
|
||||
:observe-form-changes="true"
|
||||
@on-data-saved="onDataSaved()"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.agencyTerms.agencyFk')"
|
||||
v-model="data.agencyFk"
|
||||
:options="agenciesOptions"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
hide-selected
|
||||
rounded
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.minimumM3')"
|
||||
v-model.number="data.minimumM3"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.packagePrice')"
|
||||
v-model.number="data.packagePrice"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.kmPrice')"
|
||||
v-model.number="data.kmPrice"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.m3Price')"
|
||||
v-model.number="data.m3Price"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.routePrice')"
|
||||
v-model.number="data.routePrice"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.agencyTerms.minimumKm')"
|
||||
v-model.number="data.minimumKm"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<div class="col" />
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</QPage>
|
||||
</template>
|
|
@ -1 +1,97 @@
|
|||
<template>Supplier basic data</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const workersOptions = ref([]);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="Workers/search"
|
||||
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
|
||||
@on-fetch="(data) => (workersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FormModel
|
||||
:url="`Suppliers/${route.params.id}`"
|
||||
:url-update="`Suppliers/${route.params.id}`"
|
||||
model="supplier"
|
||||
auto-load
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.nickname"
|
||||
:label="t('supplier.basicData.alias')"
|
||||
:rules="validate('supplier.nickname')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.basicData.workerFk')"
|
||||
v-model="data.workerFk"
|
||||
:options="workersOptions"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
map-options
|
||||
:rules="validate('supplier.workerFk')"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isSerious"
|
||||
:label="t('supplier.basicData.isSerious')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isActive"
|
||||
:label="t('supplier.basicData.isActive')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCheckbox
|
||||
v-model="data.isPayMethodChecked"
|
||||
:label="t('supplier.basicData.isPayMethodChecked')"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.basicData.note')"
|
||||
type="textarea"
|
||||
v-model="data.note"
|
||||
fill-input
|
||||
autogrow
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
|
|
@ -1 +1,68 @@
|
|||
<template>Supplier billing data</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const paymethodsOptions = ref([]);
|
||||
const payDemsOptions = ref([]);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="Paymethods"
|
||||
@on-fetch="(data) => (paymethodsOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData url="PayDems" @on-fetch="(data) => (payDemsOptions = data)" auto-load />
|
||||
<FormModel
|
||||
:url="`Suppliers/${route.params.id}`"
|
||||
:url-update="`Suppliers/${route.params.id}`"
|
||||
model="supplier"
|
||||
auto-load
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.billingData.payMethodFk')"
|
||||
v-model="data.payMethodFk"
|
||||
:options="paymethodsOptions"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
map-options
|
||||
:rules="validate('supplier.payMethodFk')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.billingData.payDemFk')"
|
||||
v-model="data.payDemFk"
|
||||
:options="payDemsOptions"
|
||||
option-value="id"
|
||||
option-label="payDem"
|
||||
hide-selected
|
||||
map-options
|
||||
:rules="validate('supplier.payDemFk')"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.billingData.payDay')"
|
||||
type="number"
|
||||
v-model="data.payDay"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
|
|
@ -29,7 +29,6 @@ const { t } = useI18n();
|
|||
<QPageContainer>
|
||||
<QPage>
|
||||
<VnSubToolbar />
|
||||
|
||||
<div class="q-pa-md"><RouterView></RouterView></div>
|
||||
</QPage>
|
||||
</QPageContainer>
|
||||
|
@ -63,3 +62,8 @@ const { t } = useI18n();
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search suppliers: Buscar proveedores
|
||||
</i18n>
|
||||
|
|
|
@ -1 +1,192 @@
|
|||
<template>Supplier consumption</template>
|
||||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||
|
||||
import { toDate, toDateString } from 'src/filters';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import axios from 'axios';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
const quasar = useQuasar();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const suppliersConsumption = ref();
|
||||
|
||||
const userParams = computed(() => {
|
||||
const minDate = Date.vnNew();
|
||||
minDate.setHours(0, 0, 0, 0);
|
||||
minDate.setMonth(minDate.getMonth() - 2);
|
||||
|
||||
const maxDate = Date.vnNew();
|
||||
maxDate.setHours(23, 59, 59, 59);
|
||||
|
||||
return {
|
||||
from: toDateString(minDate),
|
||||
to: toDateString(maxDate),
|
||||
};
|
||||
});
|
||||
|
||||
const reportParams = computed(() => ({
|
||||
recipientId: Number(route.params.id),
|
||||
...userParams.value,
|
||||
}));
|
||||
|
||||
const rows = computed(() => suppliersConsumption.value || []);
|
||||
|
||||
const openReportPdf = () => {
|
||||
openReport(`Suppliers/${route.params.id}/campaign-metrics-pdf`, reportParams.value);
|
||||
};
|
||||
|
||||
const getSupplierContacts = async () => {
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
where: {
|
||||
supplierFk: route.params.id,
|
||||
email: { neq: null },
|
||||
},
|
||||
limit: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const { data } = await axios.get('SupplierContacts', params);
|
||||
|
||||
if (data && data.length) return data[0].email;
|
||||
} catch (err) {
|
||||
notify('This supplier does not have a contact with an email address', 'negative');
|
||||
console.error('Error fetching supplierContacts: ', err);
|
||||
}
|
||||
};
|
||||
|
||||
const openSendEmailDialog = async () => {
|
||||
const email = await getSupplierContacts();
|
||||
quasar.dialog({
|
||||
component: SendEmailDialog,
|
||||
componentProps: {
|
||||
data: {
|
||||
address: email,
|
||||
},
|
||||
promise: sendCampaignMetricsEmail,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const sendCampaignMetricsEmail = ({ address }) => {
|
||||
sendEmail(`Suppliers/${route.params.id}/campaign-metrics-email`, {
|
||||
recipient: address,
|
||||
...reportParams.value,
|
||||
});
|
||||
};
|
||||
|
||||
const calculateTotal = (buysArray) => {
|
||||
return buysArray.reduce((accumulator, { total }) => accumulator + total, 0);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers/consumption"
|
||||
@on-fetch="(data) => (suppliersConsumption = data)"
|
||||
:filter="{
|
||||
where: { supplierFk: route.params.id },
|
||||
order: ['itemTypeFk', 'itemName', 'itemSize'],
|
||||
}"
|
||||
:params="userParams"
|
||||
auto-load
|
||||
/>
|
||||
<QToolbar class="bg-vn-dark justify-end">
|
||||
<div id="st-data">
|
||||
<QBtn
|
||||
v-if="userParams.from && userParams.to"
|
||||
color="primary"
|
||||
icon-right="picture_as_pdf"
|
||||
no-caps
|
||||
class="q-mr-md"
|
||||
@click="openReportPdf()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Open as PDF') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
v-if="userParams.from && userParams.to"
|
||||
color="primary"
|
||||
icon-right="email"
|
||||
no-caps
|
||||
@click="openSendEmailDialog()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Send to email') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
<QSpace />
|
||||
<div id="st-actions"></div>
|
||||
</QToolbar>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<QTable
|
||||
:rows="rows"
|
||||
hide-bottom
|
||||
row-key="id"
|
||||
hide-header
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
class="full-width q-mt-md"
|
||||
>
|
||||
<template #body="{ row }">
|
||||
<QTr>
|
||||
<QTd no-hover class="label">{{
|
||||
t('supplier.consumption.entry')
|
||||
}}</QTd>
|
||||
<QTd no-hover>{{ row.id }}</QTd>
|
||||
<QTd no-hover class="label">{{ t('supplier.consumption.date') }}</QTd>
|
||||
<QTd no-hover>{{ toDate(row.shipped) }}</QTd>
|
||||
<QTd no-hover class="label">{{
|
||||
t('supplier.consumption.reference')
|
||||
}}</QTd>
|
||||
<QTd no-hover>{{ row.invoiceNumber }}</QTd>
|
||||
</QTr>
|
||||
<QTr v-for="(buy, index) in row.buys" :key="index">
|
||||
<QTd no-hover> {{ buy.itemName }}</QTd>
|
||||
|
||||
<QTd no-hover>
|
||||
<span>{{ buy.subName }}</span>
|
||||
<fetched-tags :item="buy" :max-length="5" />
|
||||
</QTd>
|
||||
<QTd no-hover> {{ dashIfEmpty(buy.quantity) }}</QTd>
|
||||
<QTd no-hover> {{ dashIfEmpty(buy.price) }}</QTd>
|
||||
<QTd colspan="2" no-hover> {{ dashIfEmpty(buy.total) }}</QTd>
|
||||
</QTr>
|
||||
<QTr>
|
||||
<QTd colspan="6" no-hover>
|
||||
<span class="label">{{ t('Total entry') }}: </span>
|
||||
<span>{{ calculateTotal(row.buys) }}</span>
|
||||
</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.label {
|
||||
color: var(--vn-label);
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Total entry: Total entrada
|
||||
Open as PDF: Abrir como PDF
|
||||
Send to email: Enviar por email
|
||||
This supplier does not have a contact with an email address: Este proveedor no tiene un email de contacto
|
||||
</i18n>
|
||||
|
|
|
@ -1 +1,118 @@
|
|||
<template>Supplier contacts</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CrudModel from 'components/CrudModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const supplierContactRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (supplierContactRef.value) supplierContactRef.value.reload();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CrudModel
|
||||
data-key="SupplierContact"
|
||||
url="SupplierContacts"
|
||||
model="SupplierContact"
|
||||
:filter="{
|
||||
where: { supplierFk: route.params.id },
|
||||
}"
|
||||
ref="supplierContactRef"
|
||||
:default-remove="false"
|
||||
:data-required="{ supplierFk: route.params.id }"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QCard class="q-pa-md">
|
||||
<QCardSection
|
||||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
class="border q-pa-md q-mb-md"
|
||||
>
|
||||
<VnRow class="row q-gutter-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('supplier.contacts.name')"
|
||||
v-model="row.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('supplier.contacts.phone')"
|
||||
v-model="row.phone"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('supplier.contacts.mobile')"
|
||||
v-model="row.mobile"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('supplier.contacts.email')"
|
||||
v-model="row.email"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md">
|
||||
<div class="col">
|
||||
<QInput
|
||||
:label="t('supplier.contacts.observation')"
|
||||
type="textarea"
|
||||
v-model="row.observation"
|
||||
fill-input
|
||||
autogrow
|
||||
/>
|
||||
</div>
|
||||
<div class="col-1 row justify-center items-center">
|
||||
<QIcon
|
||||
name="delete"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="supplierContactRef.remove([row])"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Remove contact') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</VnRow>
|
||||
</QCardSection>
|
||||
<VnRow>
|
||||
<QIcon
|
||||
name="add"
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
@click="supplierContactRef.insert()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Add contact') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
</template>
|
||||
</CrudModel>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.border {
|
||||
border-radius: 0px !important;
|
||||
border: 1px solid var(--vn-text) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Add contact: Añadir contacto
|
||||
Remove contact: Remover contacto
|
||||
</i18n>
|
||||
|
|
|
@ -1 +1,298 @@
|
|||
<template>Supplier fiscal data</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const postcodeFetchDataRef = ref(null);
|
||||
const townsFetchDataRef = ref(null);
|
||||
const sageTaxTypesOptions = ref([]);
|
||||
const sageWithholdingsOptions = ref([]);
|
||||
const sageTransactionTypesOptions = ref([]);
|
||||
const supplierActivitiesOptions = ref([]);
|
||||
const postcodesOptions = ref([]);
|
||||
const provincesLocationOptions = ref([]);
|
||||
const townsLocationOptions = ref([]);
|
||||
const countriesOptions = ref([]);
|
||||
|
||||
const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formData) => {
|
||||
await postcodeFetchDataRef.value.fetch();
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.postCode = code;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.city = townsLocationOptions.value.find((town) => town.id === townFk).name;
|
||||
formData.countryFk = countryFk;
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="SageTaxTypes"
|
||||
auto-load
|
||||
@on-fetch="(data) => (sageTaxTypesOptions = data)"
|
||||
/>
|
||||
<FetchData
|
||||
url="SageWithholdings"
|
||||
auto-load
|
||||
@on-fetch="(data) => (sageWithholdingsOptions = data)"
|
||||
/>
|
||||
<FetchData
|
||||
url="sageTransactionTypes"
|
||||
auto-load
|
||||
@on-fetch="(data) => (sageTransactionTypesOptions = data)"
|
||||
/>
|
||||
<FetchData
|
||||
url="SupplierActivities"
|
||||
auto-load
|
||||
@on-fetch="(data) => (supplierActivitiesOptions = data)"
|
||||
/>
|
||||
<FetchData
|
||||
ref="postcodeFetchDataRef"
|
||||
url="Postcodes/location"
|
||||
@on-fetch="(data) => (postcodesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="townsFetchDataRef"
|
||||
@on-fetch="(data) => (townsLocationOptions = data)"
|
||||
auto-load
|
||||
url="Towns/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||
auto-load
|
||||
url="Provinces/location"
|
||||
/>
|
||||
<FetchData
|
||||
@on-fetch="(data) => (countriesOptions = data)"
|
||||
auto-load
|
||||
url="Countries"
|
||||
/>
|
||||
<FormModel
|
||||
:url="`Suppliers/${route.params.id}`"
|
||||
:url-update="`Suppliers/${route.params.id}/updateFiscalData`"
|
||||
model="supplier"
|
||||
auto-load
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.name"
|
||||
:label="t('supplier.fiscalData.name')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.nif"
|
||||
:label="t('supplier.fiscalData.nif')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.account"
|
||||
:label="t('supplier.fiscalData.account')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.sageTaxTypeFk')"
|
||||
v-model="data.sageTaxTypeFk"
|
||||
:options="sageTaxTypesOptions"
|
||||
option-value="id"
|
||||
option-label="vat"
|
||||
hide-selected
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.sageWithholdingFk')"
|
||||
v-model="data.sageWithholdingFk"
|
||||
:options="sageWithholdingsOptions"
|
||||
option-value="id"
|
||||
option-label="withholding"
|
||||
hide-selected
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.sageTransactionTypeFk')"
|
||||
v-model="data.sageTransactionTypeFk"
|
||||
:options="sageTransactionTypesOptions"
|
||||
option-value="id"
|
||||
option-label="transaction"
|
||||
hide-selected
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.supplierActivityFk')"
|
||||
v-model="data.supplierActivityFk"
|
||||
:options="supplierActivitiesOptions"
|
||||
option-value="code"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.healthRegister"
|
||||
:label="t('supplier.fiscalData.healthRegister')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
v-model="data.street"
|
||||
:label="t('supplier.fiscalData.street')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectCreate
|
||||
:label="t('supplier.fiscalData.postcode')"
|
||||
v-model="data.postCode"
|
||||
:options="postcodesOptions"
|
||||
:rules="validate('supplier.postCode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
option-label="code"
|
||||
option-value="code"
|
||||
hide-selected
|
||||
>
|
||||
<template #form>
|
||||
<CustomerCreateNewPostcode
|
||||
@on-data-saved="onPostcodeCreated($event, data)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection v-if="scope.opt">
|
||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||
<QItemLabel caption
|
||||
>{{ scope.opt.code }} -
|
||||
{{ scope.opt.town.name }} ({{
|
||||
scope.opt.town.province.name
|
||||
}},
|
||||
{{
|
||||
scope.opt.town.province.country.country
|
||||
}})</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectCreate>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.city')"
|
||||
:options="townsLocationOptions"
|
||||
v-model="data.city"
|
||||
option-value="name"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
:rules="validate('supplier.city')"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||
<QItemLabel caption
|
||||
>{{ scope.opt.name }},
|
||||
{{ scope.opt.province.name }} ({{
|
||||
scope.opt.province.country.country
|
||||
}})</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.provinceFk')"
|
||||
:options="provincesLocationOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="data.provinceFk"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt.name} (${scope.opt.country.country})`
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnSelectFilter
|
||||
:label="t('supplier.fiscalData.country')"
|
||||
:options="countriesOptions"
|
||||
hide-selected
|
||||
option-label="country"
|
||||
option-value="id"
|
||||
v-model="data.countryFk"
|
||||
:rules="validate('postcode.countryFk')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col flex justify-around">
|
||||
<QCheckbox
|
||||
v-model="data.isTrucker"
|
||||
:label="t('supplier.fiscalData.isTrucker')"
|
||||
/>
|
||||
<div class="row items-center">
|
||||
<QCheckbox
|
||||
v-model="data.isVies"
|
||||
:label="t('supplier.fiscalData.isVies')"
|
||||
/>
|
||||
<QIcon name="info" size="xs" class="cursor-pointer q-ml-sm">
|
||||
<QTooltip>
|
||||
{{
|
||||
t(
|
||||
'When activating it, do not enter the country code in the ID field.'
|
||||
)
|
||||
}}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
When activating it, do not enter the country code in the ID field.: Al activarlo, no informar el código del país en el campo nif
|
||||
</i18n>
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
<template>Supplier log</template>
|
||||
<script setup>
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
</script>
|
||||
<template>
|
||||
<VnLog model="Supplier" url="/SupplierLogs"></VnLog>
|
||||
</template>
|
||||
|
|
|
@ -182,4 +182,3 @@ const isAdministrative = computed(() => {
|
|||
</template>
|
||||
</CardSummary>
|
||||
</template>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
@ -6,7 +6,6 @@ import { useI18n } from 'vue-i18n';
|
|||
import { QCheckbox, QIcon } from 'quasar';
|
||||
import CardSummary from 'components/ui/CardSummary.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
import travelService from 'src/services/travel.service';
|
||||
import { toDate, toCurrency } from 'src/filters';
|
||||
|
@ -218,21 +217,12 @@ const openEntryDescriptor = () => {};
|
|||
</template>
|
||||
|
||||
<template #body>
|
||||
<QCard class="vn-one row justify-around" style="min-width: 100%">
|
||||
<VnRow>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('globals.shipped')"
|
||||
:value="toDate(travel.shipped)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<QCard class="vn-one">
|
||||
<VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" />
|
||||
<VnLv
|
||||
:label="t('globals.wareHouseOut')"
|
||||
:value="travel.warehouseOut?.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv :label="t('travel.summary.delivered')" class="q-mb-xs">
|
||||
<template #value>
|
||||
<QCheckbox
|
||||
|
@ -243,22 +233,13 @@ const openEntryDescriptor = () => {};
|
|||
/>
|
||||
</template>
|
||||
</VnLv>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<div class="col">
|
||||
<VnLv
|
||||
:label="t('globals.landed')"
|
||||
:value="toDate(travel.landed)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" />
|
||||
<VnLv
|
||||
:label="t('globals.wareHouseIn')"
|
||||
:value="travel.warehouseIn?.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv :label="t('travel.summary.received')" class="q-mb-xs">
|
||||
<template #value>
|
||||
<QCheckbox
|
||||
|
@ -269,24 +250,14 @@ const openEntryDescriptor = () => {};
|
|||
/>
|
||||
</template>
|
||||
</VnLv>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<div class="col">
|
||||
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv label="m³" :value="travel.m3" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<VnLv :label="t('globals.totalEntries')" :value="travel.m3" />
|
||||
</div>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
<QCard class="vn-two" v-if="entriesTableRows.length > 0">
|
||||
<QCard class="vn-one">
|
||||
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" />
|
||||
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
||||
<VnLv label="m³" :value="travel.m3" />
|
||||
<VnLv :label="t('globals.totalEntries')" :value="travel.m3" />
|
||||
</QCard>
|
||||
<QCard class="full-width" v-if="entriesTableRows.length > 0">
|
||||
<a class="header" :href="travelUrl + 'entry'">
|
||||
{{ t('travel.summary.entries') }}
|
||||
<QIcon name="open_in_new" color="primary" />
|
||||
|
|
|
@ -9,6 +9,7 @@ import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.v
|
|||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import ExtraCommunityFilter from './ExtraCommunityFilter.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import EntryDescriptorProxy from '../Entry/Card/EntryDescriptorProxy.vue';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { toCurrency } from 'src/filters';
|
||||
|
@ -231,7 +232,7 @@ const navigateToTravelId = (id) => {
|
|||
};
|
||||
|
||||
const stopEventPropagation = (event, col) => {
|
||||
if (!['ref', 'id', 'supplier'].includes(col.name)) return;
|
||||
if (!['ref', 'id', 'cargoSupplierNickname'].includes(col.name)) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
@ -262,7 +263,16 @@ onMounted(async () => {
|
|||
<div id="st-data"></div>
|
||||
<QSpace />
|
||||
<div id="st-actions">
|
||||
<QBtn color="primary" icon-right="archive" no-caps @click="openReportPdf()" />
|
||||
<QBtn
|
||||
color="primary"
|
||||
icon-right="picture_as_pdf"
|
||||
no-caps
|
||||
@click="openReportPdf()"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Open as PDF') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</QToolbar>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
|
@ -350,10 +360,9 @@ onMounted(async () => {
|
|||
:props="props"
|
||||
class="secondary-row"
|
||||
>
|
||||
<QTd
|
||||
><QBtn flat color="blue" class="col-content">{{ entry.id }}</QBtn>
|
||||
<!-- Cuando se cree el modulo relacionado a entries, crear su descriptor y colocarlo acá -->
|
||||
<!-- <EntryDescriptorProxy :id="entry.id"/> -->
|
||||
<QTd>
|
||||
<QBtn flat color="blue" class="col-content">{{ entry.id }} </QBtn>
|
||||
<EntryDescriptorProxy :id="entry.id" />
|
||||
</QTd>
|
||||
<QTd
|
||||
><QBtn flat color="blue" class="col-content">{{
|
||||
|
@ -415,4 +424,5 @@ es:
|
|||
physicKg: KG físico
|
||||
shipped: F. envío
|
||||
landed: F. llegada
|
||||
Open as PDF: Abrir como PDF
|
||||
</i18n>
|
||||
|
|
|
@ -50,6 +50,7 @@ const newWorkerForm = ref({
|
|||
});
|
||||
|
||||
const postcodeFetchDataRef = ref(null);
|
||||
const townsFetchDataRef = ref(null);
|
||||
const provincesOptions = ref([]);
|
||||
const townsOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
|
@ -66,8 +67,12 @@ const onBankEntityCreated = (data) => {
|
|||
bankEntitiesOptions.value.push(data);
|
||||
};
|
||||
|
||||
const onPostcodeCreated = async () => {
|
||||
postcodeFetchDataRef.value.fetch();
|
||||
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
||||
await postcodeFetchDataRef.value.fetch();
|
||||
await townsFetchDataRef.value.fetch();
|
||||
formData.postcode = code;
|
||||
formData.provinceFk = provinceFk;
|
||||
formData.city = townsOptions.value.find((town) => town.id === townFk).name;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
|
@ -96,6 +101,7 @@ onMounted(async () => {
|
|||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="townsFetchDataRef"
|
||||
url="Towns/location"
|
||||
@on-fetch="(data) => (townsOptions = data)"
|
||||
:filter="townsFilter"
|
||||
|
|
|
@ -134,6 +134,12 @@ export default {
|
|||
component: () =>
|
||||
import('src/pages/Supplier/Card/SupplierAddresses.vue'),
|
||||
},
|
||||
{
|
||||
path: 'address/create',
|
||||
name: 'SupplierAddressesCreate',
|
||||
component: () =>
|
||||
import('src/pages/Supplier/Card/SupplierAddressesCreate.vue'),
|
||||
},
|
||||
{
|
||||
path: 'consumption',
|
||||
name: 'SupplierConsumption',
|
||||
|
@ -154,6 +160,12 @@ export default {
|
|||
component: () =>
|
||||
import('src/pages/Supplier/Card/SupplierAgencyTerm.vue'),
|
||||
},
|
||||
{
|
||||
path: 'agency-term/create',
|
||||
name: 'SupplierAgencyTermCreate',
|
||||
component: () =>
|
||||
import('src/pages/Supplier/Card/SupplierAgencyTermCreate.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -152,25 +152,85 @@ export default {
|
|||
},
|
||||
{
|
||||
path: 'consignees',
|
||||
name: 'ConsigneesCard',
|
||||
redirect: { name: 'CustomerConsignees' },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'CustomerConsignees',
|
||||
meta: {
|
||||
title: 'consignees',
|
||||
icon: 'vn:delivery',
|
||||
title: 'consignees',
|
||||
},
|
||||
component: () =>
|
||||
import('src/pages/Customer/Card/CustomerConsignees.vue'),
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'CustomerConsigneeCreate',
|
||||
meta: {
|
||||
title: 'consignee-create',
|
||||
},
|
||||
component: () =>
|
||||
import(
|
||||
'src/pages/Customer/components/CustomerConsigneeCreate.vue'
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ':consigneeId',
|
||||
name: 'CustomerConsigneeEditCard',
|
||||
redirect: { name: 'CustomerConsigneeEdit' },
|
||||
children: [
|
||||
{
|
||||
path: 'edit',
|
||||
name: 'CustomerConsigneeEdit',
|
||||
meta: {
|
||||
title: 'consignee-edit',
|
||||
},
|
||||
component: () =>
|
||||
import(
|
||||
'src/pages/Customer/components/CustomerConsigneeEdit.vue'
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'notes',
|
||||
name: 'NotesCard',
|
||||
redirect: { name: 'CustomerNotes' },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'CustomerNotes',
|
||||
meta: {
|
||||
title: 'notes',
|
||||
icon: 'vn:notes',
|
||||
},
|
||||
component: () => import('src/pages/Customer/Card/CustomerNotes.vue'),
|
||||
component: () =>
|
||||
import('src/pages/Customer/Card/CustomerNotes.vue'),
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'CustomerNoteCreate',
|
||||
meta: {
|
||||
title: 'note-create',
|
||||
},
|
||||
component: () =>
|
||||
import(
|
||||
'src/pages/Customer/components/CustomerNoteCreate.vue'
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'credits',
|
||||
name: 'CreditsCard',
|
||||
redirect: { name: 'CustomerCredits' },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'CustomerCredits',
|
||||
meta: {
|
||||
title: 'credits',
|
||||
|
@ -179,6 +239,19 @@ export default {
|
|||
component: () =>
|
||||
import('src/pages/Customer/Card/CustomerCredits.vue'),
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'CustomerCreditCreate',
|
||||
meta: {
|
||||
title: 'credit-create',
|
||||
},
|
||||
component: () =>
|
||||
import(
|
||||
'src/pages/Customer/components/CustomerCreditCreate.vue'
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'greuges',
|
||||
name: 'CustomerGreuges',
|
||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
|||
redirect: { name: 'EntryMain' },
|
||||
menus: {
|
||||
main: ['EntryList'],
|
||||
card: [],
|
||||
card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryLog'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
@ -39,23 +39,63 @@ export default {
|
|||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: 'EntryCard',
|
||||
// path: ':id',
|
||||
// component: () => import('src/pages/Entry/Card/EntryCard.vue'),
|
||||
// redirect: { name: 'EntrySummary' },
|
||||
// children: [
|
||||
// {
|
||||
// name: 'EntrySummary',
|
||||
// path: 'summary',
|
||||
// meta: {
|
||||
// title: 'summary',
|
||||
// icon: 'launch',
|
||||
// },
|
||||
// component: () =>
|
||||
// import('src/pages/Entry/Card/EntrySummary.vue'),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
name: 'EntryCard',
|
||||
path: ':id',
|
||||
component: () => import('src/pages/Entry/Card/EntryCard.vue'),
|
||||
redirect: { name: 'EntrySummary' },
|
||||
children: [
|
||||
{
|
||||
name: 'EntrySummary',
|
||||
path: 'summary',
|
||||
meta: {
|
||||
title: 'summary',
|
||||
icon: 'launch',
|
||||
},
|
||||
component: () => import('src/pages/Entry/Card/EntrySummary.vue'),
|
||||
},
|
||||
{
|
||||
path: 'basic-data',
|
||||
name: 'EntryBasicData',
|
||||
meta: {
|
||||
title: 'basicData',
|
||||
icon: 'vn:settings',
|
||||
},
|
||||
component: () => import('src/pages/Entry/Card/EntryBasicData.vue'),
|
||||
},
|
||||
{
|
||||
path: 'buys',
|
||||
name: 'EntryBuys',
|
||||
meta: {
|
||||
title: 'buys',
|
||||
icon: 'vn:lines',
|
||||
},
|
||||
component: () => import('src/pages/Entry/Card/EntryBuys.vue'),
|
||||
},
|
||||
{
|
||||
path: 'buys/import',
|
||||
name: 'EntryBuysImport',
|
||||
component: () => import('src/pages/Entry/Card/EntryBuysImport.vue'),
|
||||
},
|
||||
{
|
||||
path: 'notes',
|
||||
name: 'EntryNotes',
|
||||
meta: {
|
||||
title: 'notes',
|
||||
icon: 'vn:notes',
|
||||
},
|
||||
component: () => import('src/pages/Entry/Card/EntryNotes.vue'),
|
||||
},
|
||||
{
|
||||
path: 'log',
|
||||
name: 'EntryLog',
|
||||
meta: {
|
||||
title: 'log',
|
||||
icon: 'vn:History',
|
||||
},
|
||||
component: () => import('src/pages/Entry/Card/EntryLog.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -10,15 +10,15 @@ export default {
|
|||
component: RouterView,
|
||||
redirect: { name: 'RouteMain' },
|
||||
menus: {
|
||||
main: ['CmrList'],
|
||||
card: [],
|
||||
main: ['RouteList', 'CmrList'],
|
||||
card: ['RouteBasicData'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/route',
|
||||
name: 'RouteMain',
|
||||
component: () => import('src/pages/Route/RouteMain.vue'),
|
||||
redirect: { name: 'CmrList' },
|
||||
redirect: { name: 'RouteList' },
|
||||
children: [
|
||||
{
|
||||
path: 'cmr',
|
||||
|
@ -29,6 +29,49 @@ export default {
|
|||
},
|
||||
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
name: 'RouteList',
|
||||
meta: {
|
||||
title: 'RouteList',
|
||||
icon: 'view_list',
|
||||
},
|
||||
component: () => import('src/pages/Route/RouteList.vue'),
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'RouteCreate',
|
||||
meta: {
|
||||
title: 'create',
|
||||
},
|
||||
component: () => import('src/pages/Route/Card/RouteForm.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'RouteCard',
|
||||
path: ':id',
|
||||
component: () => import('src/pages/Route/Card/RouteCard.vue'),
|
||||
redirect: { name: 'RouteSummary' },
|
||||
children: [
|
||||
{
|
||||
name: 'RouteBasicData',
|
||||
path: 'basic-data',
|
||||
meta: {
|
||||
title: 'basicData',
|
||||
icon: 'vn:settings',
|
||||
},
|
||||
component: () => import('pages/Route/Card/RouteForm.vue'),
|
||||
},
|
||||
{
|
||||
name: 'RouteSummary',
|
||||
path: 'summary',
|
||||
meta: {
|
||||
title: 'summary',
|
||||
icon: 'open_in_new',
|
||||
},
|
||||
component: () => import('pages/Route/Card/RouteSummary.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue