Merge pull request 'feature/SupplierSubmodules-2' (#66) from feature/SupplierSubmodules-2 into dev

Reviewed-on: hyervoni/salix-front-mindshore#66
This commit is contained in:
William Buezas 2024-01-11 20:29:57 +00:00
commit 3905dd05df
15 changed files with 1126 additions and 11 deletions

View File

@ -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>

View File

@ -131,7 +131,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

View File

@ -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;
}

View File

@ -907,6 +907,41 @@ export default {
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',
},
consumption: {
entry: 'Entry',
date: 'Date',
reference: 'Reference',
},
},
travel: {
pageTitles: {

View File

@ -906,6 +906,41 @@ export default {
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',
},
consumption: {
entry: 'Entrada',
date: 'Fecha',
reference: 'Referencia',
},
},
travel: {
pageTitles: {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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.list.newSupplier') }}
</QTooltip>
</QPageSticky>
</template>
<i18n>
es:
Remove row: Eliminar fila
</i18n>

View File

@ -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>

View File

@ -29,7 +29,6 @@ const { t } = useI18n();
<QPageContainer>
<QPage>
<VnSubToolbar />
<div class="q-pa-md"><RouterView></RouterView></div>
</QPage>
</QPageContainer>

View File

@ -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>

View File

@ -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>

View File

@ -262,7 +262,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>
@ -415,4 +424,5 @@ es:
physicKg: KG físico
shipped: F. envío
landed: F. llegada
Open as PDF: Abrir como PDF
</i18n>

View File

@ -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'),
},
],
},
],