0
0
Fork 0

Merge branch 'dev' into 6151-FixOrderModule

This commit is contained in:
Jon Elias 2024-07-03 06:48:33 +00:00
commit 467ec08f3e
45 changed files with 1489 additions and 482 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-front", "name": "salix-front",
"version": "24.28.1", "version": "24.30.1",
"description": "Salix frontend", "description": "Salix frontend",
"productName": "Salix", "productName": "Salix",
"author": "Verdnatura", "author": "Verdnatura",

BIN
public/no-image-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
public/no-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
public/no-user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -58,6 +58,7 @@ function addChildren(module, route, parent) {
} }
const items = ref([]); const items = ref([]);
function getRoutes() { function getRoutes() {
if (props.source === 'main') { if (props.source === 'main') {
const modules = Object.assign([], navigation.getModules().value); const modules = Object.assign([], navigation.getModules().value);
@ -66,9 +67,8 @@ function getRoutes() {
const moduleDef = routes.find( const moduleDef = routes.find(
(route) => toLowerCamel(route.name) === item.module (route) => toLowerCamel(route.name) === item.module
); );
item.children = [];
if (!moduleDef) continue; if (!moduleDef) continue;
item.children = [];
addChildren(item.module, moduleDef, item.children); addChildren(item.module, moduleDef, item.children);
} }

View File

@ -1,21 +1,19 @@
<script setup> <script setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useSession } from 'src/composables/useSession';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import PinnedModules from './PinnedModules.vue'; import PinnedModules from './PinnedModules.vue';
import UserPanel from 'components/UserPanel.vue'; import UserPanel from 'components/UserPanel.vue';
import VnBreadcrumbs from './common/VnBreadcrumbs.vue'; import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
import VnImg from 'src/components/ui/VnImg.vue';
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
const quasar = useQuasar(); const quasar = useQuasar();
const state = useState(); const state = useState();
const user = state.getUser(); const user = state.getUser();
const { getTokenMultimedia } = useSession();
const token = getTokenMultimedia();
const appName = 'Lilium'; const appName = 'Lilium';
onMounted(() => stateStore.setMounted()); onMounted(() => stateStore.setMounted());
@ -83,11 +81,12 @@ const pinnedModulesRef = ref();
id="user" id="user"
> >
<QAvatar size="lg"> <QAvatar size="lg">
<QImg <VnImg
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`" :id="user.id"
spinner-color="primary" collection="user"
> size="160x160"
</QImg> :zoom-size="null"
/>
</QAvatar> </QAvatar>
<QTooltip bottom> <QTooltip bottom>
{{ t('globals.userPanel') }} {{ t('globals.userPanel') }}

View File

@ -12,12 +12,14 @@ import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import { useClipboard } from 'src/composables/useClipboard'; import { useClipboard } from 'src/composables/useClipboard';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
import { useRole } from 'src/composables/useRole';
const state = useState(); const state = useState();
const session = useSession(); const session = useSession();
const router = useRouter(); const router = useRouter();
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const { copyText } = useClipboard(); const { copyText } = useClipboard();
const userLocale = computed({ const userLocale = computed({
get() { get() {
return locale.value; return locale.value;
@ -99,6 +101,7 @@ function saveUserData(param, value) {
axios.post('UserConfigs/setUserConfig', { [param]: value }); axios.post('UserConfigs/setUserConfig', { [param]: value });
localUserData(); localUserData();
} }
const isEmployee = computed(() => useRole().isEmployee());
</script> </script>
<template> <template>
@ -109,12 +112,14 @@ function saveUserData(param, value) {
auto-load auto-load
/> />
<FetchData <FetchData
v-if="isEmployee"
url="Companies" url="Companies"
order="name" order="name"
@on-fetch="(data) => (companiesData = data)" @on-fetch="(data) => (companiesData = data)"
auto-load auto-load
/> />
<FetchData <FetchData
v-if="isEmployee"
url="Accountings" url="Accountings"
order="name" order="name"
@on-fetch="(data) => (accountBankData = data)" @on-fetch="(data) => (accountBankData = data)"

View File

@ -59,6 +59,10 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
hasSubtoolbar: {
type: Boolean,
default: true,
},
}); });
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -175,11 +179,14 @@ function columnName(col) {
} }
function getColAlign(col) { function getColAlign(col) {
return 'text-' + (col.align ?? 'left') return 'text-' + (col.align ?? 'left');
} }
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
defineExpose({ defineExpose({
reload, reload,
redirect: redirectFn, redirect: redirectFn,
selected,
}); });
</script> </script>
<template> <template>
@ -225,11 +232,18 @@ defineExpose({
:search-url="searchUrl" :search-url="searchUrl"
:disable-infinite-scroll="mode == TABLE_MODE" :disable-infinite-scroll="mode == TABLE_MODE"
@save-changes="reload" @save-changes="reload"
:has-subtoolbar="isEditable" :has-subtoolbar="$attrs['hasSubtoolbar'] ?? isEditable"
> >
<template
v-for="(_, slotName) in $slots"
#[slotName]="slotData"
:key="slotName"
>
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
</template>
<template #body="{ rows }"> <template #body="{ rows }">
<QTable <QTable
v-bind="$attrs['QTable']" v-bind="$attrs['q-table']"
class="vnTable" class="vnTable"
:columns="splittedColumns.columns" :columns="splittedColumns.columns"
:rows="rows" :rows="rows"
@ -246,6 +260,7 @@ defineExpose({
CrudModelRef.vnPaginateRef.paginate() CrudModelRef.vnPaginateRef.paginate()
" "
@row-click="(_, row) => rowClickFunction(row)" @row-click="(_, row) => rowClickFunction(row)"
@update:selected="emit('update:selected', $event)"
> >
<template #top-left> <template #top-left>
<slot name="top-left"></slot> <slot name="top-left"></slot>
@ -310,6 +325,7 @@ defineExpose({
class="no-margin q-px-xs" class="no-margin q-px-xs"
:class="getColAlign(col)" :class="getColAlign(col)"
> >
<slot :name="`column-${col.name}`" :col="col" :row="row">
<VnTableColumn <VnTableColumn
:column="col" :column="col"
:row="row" :row="row"
@ -317,6 +333,7 @@ defineExpose({
v-model="row[col.name]" v-model="row[col.name]"
component-prop="columnField" component-prop="columnField"
/> />
</slot>
</QTd> </QTd>
</template> </template>
<template #body-cell-tableActions="{ col, row }"> <template #body-cell-tableActions="{ col, row }">
@ -411,6 +428,11 @@ defineExpose({
@click=" @click="
stopEventPropagation($event) stopEventPropagation($event)
" "
>
<slot
:name="`column-${col.name}`"
:col="col"
:row="row"
> >
<VnTableColumn <VnTableColumn
:column="col" :column="col"
@ -420,6 +442,7 @@ defineExpose({
component-prop="columnField" component-prop="columnField"
:show-label="true" :show-label="true"
/> />
</slot>
</span> </span>
</template> </template>
</VnLv> </VnLv>
@ -477,6 +500,7 @@ defineExpose({
default="input" default="input"
v-model="data[column.name]" v-model="data[column.name]"
:show-label="true" :show-label="true"
component-prop="columnCreate"
/> />
<slot name="more-create-dialog" :data="data" /> <slot name="more-create-dialog" :data="data" />
</div> </div>

View File

@ -93,7 +93,12 @@ const inputRules = [
name="close" name="close"
size="xs" size="xs"
v-if="hover && value && !$attrs.disabled && $props.clearable" v-if="hover && value && !$attrs.disabled && $props.clearable"
@click="value = null" @click="
() => {
value = null;
emit('remove');
}
"
></QIcon> ></QIcon>
<QIcon v-if="info" name="info"> <QIcon v-if="info" name="info">
<QTooltip max-width="350px"> <QTooltip max-width="350px">

View File

@ -61,6 +61,10 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
useLike: {
type: Boolean,
default: true,
},
}); });
const { t } = useI18n(); const { t } = useI18n();
@ -114,11 +118,14 @@ async function fetchFilter(val) {
if (!$props.url || !dataRef.value) return; if (!$props.url || !dataRef.value) return;
const { fields, sortBy, limit } = $props; const { fields, sortBy, limit } = $props;
let key = optionLabel.value; let key = optionFilter.value ?? optionLabel.value;
if (new RegExp(/\d/g).test(val)) key = optionFilter.value ?? optionValue.value; if (new RegExp(/\d/g).test(val)) key = optionValue.value;
const where = { ...{ [key]: { like: `%${val}%` } }, ...$props.where }; const defaultWhere = $props.useLike
? { [key]: { like: `%${val}%` } }
: { [key]: val };
const where = { ...defaultWhere, ...$props.where };
const fetchOptions = { where, order: sortBy, limit }; const fetchOptions = { where, order: sortBy, limit };
if (fields) fetchOptions.fields = fields; if (fields) fetchOptions.fields = fields;
return dataRef.value.fetch(fetchOptions); return dataRef.value.fetch(fetchOptions);

View File

@ -10,7 +10,7 @@ const { t } = useI18n();
const $props = defineProps({ const $props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: () => {} default: () => {},
}, },
dataKey: { dataKey: {
type: String, type: String,
@ -58,7 +58,14 @@ const $props = defineProps({
}, },
}); });
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']); const emit = defineEmits([
'update:modelValue',
'refresh',
'clear',
'search',
'init',
'remove',
]);
const arrayData = useArrayData($props.dataKey, { const arrayData = useArrayData($props.dataKey, {
exprBuilder: $props.exprBuilder, exprBuilder: $props.exprBuilder,
@ -67,9 +74,9 @@ const arrayData = useArrayData($props.dataKey, {
}); });
const route = useRoute(); const route = useRoute();
const store = arrayData.store; const store = arrayData.store;
const userParams = ref({}) const userParams = ref({});
onMounted(() => { onMounted(() => {
userParams.value = $props.modelValue ?? {} userParams.value = $props.modelValue ?? {};
emit('init', { params: userParams.value }); emit('init', { params: userParams.value });
}); });
@ -92,6 +99,11 @@ watch(
(val) => setUserParams(val) (val) => setUserParams(val)
); );
watch(
() => $props.modelValue,
(val) => (userParams.value = val ?? {})
);
const isLoading = ref(false); const isLoading = ref(false);
async function search(evt) { async function search(evt) {
if (evt && $props.disableSubmitEvent) return; if (evt && $props.disableSubmitEvent) return;
@ -145,6 +157,7 @@ async function clearFilters() {
isLoading.value = false; isLoading.value = false;
emit('clear'); emit('clear');
emit('update:modelValue', userParams.value);
} }
const tagsList = computed(() => { const tagsList = computed(() => {
@ -168,6 +181,7 @@ async function remove(key) {
userParams.value[key] = undefined; userParams.value[key] = undefined;
search(); search();
emit('remove', key); emit('remove', key);
emit('update:modelValue', userParams.value);
} }
function formatValue(value) { function formatValue(value) {

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed } from 'vue';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
const $props = defineProps({ const $props = defineProps({
@ -28,21 +28,29 @@ const $props = defineProps({
const show = ref(false); const show = ref(false);
const token = useSession().getTokenMultimedia(); const token = useSession().getTokenMultimedia();
const timeStamp = ref(`timestamp=${Date.now()}`); const timeStamp = ref(`timestamp=${Date.now()}`);
const url = computed( import noImage from '/public/no-user.png';
() => import { useRole } from 'src/composables/useRole';
`/api/${$props.storage}/${$props.collection}/${$props.size}/${$props.id}/download?access_token=${token}&${timeStamp.value}` const url = computed(() => {
); const isEmployee = useRole().isEmployee();
return isEmployee
? `/api/${$props.storage}/${$props.collection}/${$props.size}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
: noImage;
});
const reload = () => { const reload = () => {
timeStamp.value = `timestamp=${Date.now()}`; timeStamp.value = `timestamp=${Date.now()}`;
}; };
defineExpose({ defineExpose({
reload, reload,
}); });
onMounted(() => {});
</script> </script>
<template> <template>
<QImg :src="url" v-bind="$attrs" @click="show = !show" spinner-color="primary" /> <QImg
:class="{ zoomIn: $props.zoomSize }"
:src="url"
v-bind="$attrs"
@click="show = !show"
spinner-color="primary"
/>
<QDialog v-model="show" v-if="$props.zoomSize"> <QDialog v-model="show" v-if="$props.zoomSize">
<QImg <QImg
:src="url" :src="url"
@ -56,7 +64,9 @@ onMounted(() => {});
<style lang="scss" scoped> <style lang="scss" scoped>
.q-img { .q-img {
&.zoomIn {
cursor: zoom-in; cursor: zoom-in;
}
min-width: 50px; min-width: 50px;
} }
.rounded { .rounded {

View File

@ -27,8 +27,12 @@ export function useRole() {
return false; return false;
} }
function isEmployee() {
return hasAny(['employee']);
}
return { return {
isEmployee,
fetch, fetch,
hasAny, hasAny,
state, state,

View File

@ -100,13 +100,143 @@ globals:
modes: Modes modes: Modes
zones: Zones zones: Zones
zonesList: Zones zonesList: Zones
deliveryList: Delivery days deliveryDays: Delivery days
upcomingList: Upcoming deliveries upcomingDeliveries: Upcoming deliveries
role: Role role: Role
alias: Alias alias: Alias
aliasUsers: Users aliasUsers: Users
subRoles: Subroles subRoles: Subroles
inheritedRoles: Inherited Roles inheritedRoles: Inherited Roles
customers: Customers
list: List
webPayments: Web Payments
extendedList: Extended list
notifications: Notifications
defaulter: Defaulter
customerCreate: New customer
fiscalData: Fiscal data
billingData: Billing data
consignees: Consignees
notes: Notes
credits: Credits
greuges: Greuges
balance: Balance
recoveries: Recoveries
webAccess: Web access
sms: Sms
creditManagement: Credit management
creditContracts: Credit contracts
creditOpinion: Credit opinion
others: Others
samples: Samples
consumption: Consumption
mandates: Mandates
contacts: Contacts
webPayment: Web payment
fileManagement: File management
unpaid: Unpaid
entries: Entries
buys: Buys
dms: File management
entryCreate: New entry
latestBuys: Latest buys
tickets: Tickets
ticketCreate: New Tickets
boxing: Boxing
sale: Sale
claims: Claims
claimCreate: New claim
lines: Lines
photos: Photos
development: Development
action: Action
invoiceOuts: Invoice out
negativeBases: Negative Bases
globalInvoicing: Global invoicing
invoiceOutCreate: Create invoice out
shelving: Shelving
shelvingList: Shelving List
shelvingCreate: New shelving
invoiceIns: Invoices In
invoiceInCreate: Create invoice in
vat: VAT
dueDay: Due day
intrastat: Intrastat
corrective: Corrective
order: Orders
orderList: List
orderCreate: New order
catalog: Catalog
volume: Volume
workers: Workers
workerCreate: New worker
department: Department
pda: PDA
pbx: Private Branch Exchange
calendar: Calendar
timeControl: Time control
locker: Locker
wagons: Wagons
wagonsList: Wagons List
wagonCreate: Create wagon
wagonEdit: Edit wagon
typesList: Types List
typeCreate: Create type
typeEdit: Edit type
wagonCounter: Trolley counter
roadmap: Roadmap
stops: Stops
routes: Routes
cmrsList: CMRs list
RouteList: List
routeCreate: New route
RouteRoadmap: Roadmaps
RouteRoadmapCreate: Create roadmap
autonomous: Autonomous
suppliers: Suppliers
supplier: Supplier
labeler: Labeler
supplierCreate: New supplier
accounts: Accounts
addresses: Addresses
agencyTerm: Agency agreement
travel: Travels
extraCommunity: Extra community
travelCreate: New travel
history: Log
thermographs: Thermograph
items: Items
diary: Diary
tags: Tags
create: Create
buyRequest: Buy requests
fixedPrice: Fixed prices
wasteBreakdown: Waste breakdown
itemCreate: New item
barcode: Barcodes
tax: Tax
botanical: Botanical
itemTypeCreate: New item type
family: Item Type
lastEntries: Last entries
itemType: Item type
monitors: Monitors
dashboard: Dashboard
users: Users
createTicket: Create ticket
ticketAdvance: Advance tickets
futureTickets: Future tickets
purchaseRequest: Purchase request
weeklyTickets: Weekly tickets
formation: Formation
locations: Locations
warehouses: Warehouses
roles: Roles
connections: Connections
acls: ACLs
mailForwarding: Mail forwarding
mailAlias: Mail alias
privileges: Privileges
created: Created created: Created
worker: Worker worker: Worker
now: Now now: Now
@ -148,40 +278,8 @@ verifyEmail:
verifyEmail: Email verification verifyEmail: Email verification
dashboard: dashboard:
pageTitles: pageTitles:
dashboard: Dashboard
customer: customer:
pageTitles:
customers: Customers
list: List
webPayments: Web Payments
extendedList: Extended list
notifications: Notifications
defaulter: Defaulter
customerCreate: New customer
summary: Summary
basicData: Basic data
fiscalData: Fiscal data
billingData: Billing data
consignees: Consignees
notes: Notes
credits: Credits
greuges: Greuges
balance: Balance
recoveries: Recoveries
webAccess: Web access
log: Log
sms: Sms
creditManagement: Credit management
creditContracts: Credit contracts
creditOpinion: Credit opinion
others: Others
samples: Samples
consumption: Consumption
mandates: Mandates
contacts: Contacts
webPayment: Web payment
fileManagement: File management
unpaid: Unpaid
list: list:
phone: Phone phone: Phone
email: Email email: Email
@ -311,17 +409,6 @@ customer:
hasCoreVnl: VNL core received hasCoreVnl: VNL core received
hasSepaVnl: VNL B2B received hasSepaVnl: VNL B2B received
entry: entry:
pageTitles:
entries: Entries
list: List
summary: Summary
basicData: Basic data
buys: Buys
notes: Notes
dms: File management
log: Log
entryCreate: New entry
latestBuys: Latest buys
list: list:
newEntry: New entry newEntry: New entry
landed: Landed landed: Landed
@ -444,10 +531,14 @@ ticket:
sms: Sms sms: Sms
notes: Notes notes: Notes
sale: Sale sale: Sale
volume: Volume
observation: Notes
ticketAdvance: Advance tickets ticketAdvance: Advance tickets
futureTickets: Future tickets futureTickets: Future tickets
purchaseRequest: Purchase request purchaseRequest: Purchase request
weeklyTickets: Weekly tickets weeklyTickets: Weekly tickets
services: Service
tracking: Tracking
list: list:
nickname: Nickname nickname: Nickname
state: State state: State
@ -522,18 +613,6 @@ ticket:
warehouse: Warehouse warehouse: Warehouse
agency: Agency agency: Agency
claim: claim:
pageTitles:
claims: Claims
list: List
claimCreate: New claim
summary: Summary
basicData: Basic Data
lines: Lines
photos: Photos
development: Development
log: Audit logs
notes: Notes
action: Action
list: list:
customer: Customer customer: Customer
assignedTo: Assigned assignedTo: Assigned
@ -597,14 +676,6 @@ claim:
noData: 'There are no images/videos, click here or drag and drop the file' noData: 'There are no images/videos, click here or drag and drop the file'
dragDrop: Drag and drop it here dragDrop: Drag and drop it here
invoiceOut: invoiceOut:
pageTitles:
invoiceOuts: Invoice out
list: List
negativeBases: Negative Bases
globalInvoicing: Global invoicing
invoiceOutCreate: Create invoice out
summary: Summary
basicData: Basic Data
list: list:
ref: Reference ref: Reference
issued: Issued issued: Issued
@ -672,13 +743,6 @@ invoiceOut:
errors: errors:
downloadCsvFailed: CSV download failed downloadCsvFailed: CSV download failed
shelving: shelving:
pageTitles:
shelving: Shelving
shelvingList: Shelving List
shelvingCreate: New shelving
summary: Summary
basicData: Basic Data
log: Logs
list: list:
parking: Parking parking: Parking
priority: Priority priority: Priority
@ -705,17 +769,6 @@ parking:
info: You can search by parking code info: You can search by parking code
label: Search parking... label: Search parking...
invoiceIn: invoiceIn:
pageTitles:
invoiceIns: Invoices In
list: List
invoiceInCreate: Create invoice in
summary: Summary
basicData: Basic data
vat: VAT
dueDay: Due day
intrastat: Intrastat
corrective: Corrective
log: Logs
list: list:
ref: Reference ref: Reference
supplier: Supplier supplier: Supplier
@ -766,15 +819,6 @@ invoiceIn:
stems: Stems stems: Stems
country: Country country: Country
order: order:
pageTitles:
order: Orders
orderList: List
orderCreate: New order
summary: Summary
basicData: Basic Data
catalog: Catalog
volume: Volume
lines: Lines
field: field:
salesPersonFk: Sales Person salesPersonFk: Sales Person
clientFk: Client clientFk: Client
@ -850,7 +894,6 @@ worker:
timeControl: Time control timeControl: Time control
locker: Locker locker: Locker
balance: Balance balance: Balance
formation: Formation
list: list:
name: Name name: Name
email: Email email: Email
@ -939,15 +982,6 @@ worker:
credit: Have credit: Have
concept: Concept concept: Concept
wagon: wagon:
pageTitles:
wagons: Wagons
wagonsList: Wagons List
wagonCreate: Create wagon
wagonEdit: Edit wagon
typesList: Types List
typeCreate: Create type
typeEdit: Edit type
wagonCounter: Trolley counter
type: type:
name: Name name: Name
submit: Submit submit: Submit
@ -976,31 +1010,7 @@ wagon:
minHeightBetweenTrays: 'The minimum height between trays is ' minHeightBetweenTrays: 'The minimum height between trays is '
maxWagonHeight: 'The maximum height of the wagon is ' maxWagonHeight: 'The maximum height of the wagon is '
uncompleteTrays: There are incomplete trays uncompleteTrays: There are incomplete trays
route/roadmap:
pageTitles:
roadmap: Roadmap
summary: Summary
basicData: Basic Data
stops: Stops
roadmap:
pageTitles:
roadmap: Roadmap
summary: Summary
basicData: Basic Data
stops: Stops
route: route:
pageTitles:
routes: Routes
cmrsList: CMRs list
RouteList: List
routeCreate: New route
basicData: Basic Data
summary: Summary
RouteRoadmap: Roadmaps
RouteRoadmapCreate: Create roadmap
tickets: Tickets
log: Log
autonomous: Autonomous
cmr: cmr:
list: list:
results: results results: results
@ -1028,22 +1038,6 @@ route:
volume: Volume volume: Volume
finished: Finished finished: Finished
supplier: supplier:
pageTitles:
suppliers: Suppliers
supplier: Supplier
list: List
supplierCreate: New supplier
summary: Summary
basicData: Basic data
fiscalData: Fiscal data
billingData: Billing data
log: Log
accounts: Accounts
contacts: Contacts
addresses: Addresses
consumption: Consumption
agencyTerm: Agency agreement
dms: File management
list: list:
payMethod: Pay method payMethod: Pay method
payDeadline: Pay deadline payDeadline: Pay deadline
@ -1135,15 +1129,6 @@ supplier:
date: Date date: Date
reference: Reference reference: Reference
travel: travel:
pageTitles:
travel: Travels
list: List
summary: Summary
extraCommunity: Extra community
travelCreate: New travel
basicData: Basic data
history: Log
thermographs: Thermograph
summary: summary:
confirmed: Confirmed confirmed: Confirmed
entryId: Entry Id entryId: Entry Id
@ -1190,24 +1175,6 @@ travel:
travelFileDescription: 'Travel id { travelId }' travelFileDescription: 'Travel id { travelId }'
file: File file: File
item: item:
pageTitles:
items: Items
list: List
diary: Diary
tags: Tags
create: Create
buyRequest: Buy requests
fixedPrice: Fixed prices
wasteBreakdown: Waste breakdown
itemCreate: New item
barcode: Barcodes
tax: Tax
log: Log
botanical: Botanical
shelving: Shelving
itemTypeCreate: New item type
family: Item Type
lastEntries: Last entries
descriptor: descriptor:
item: Item item: Item
buyer: Buyer buyer: Buyer
@ -1293,22 +1260,6 @@ item:
minSalesQuantity: 'Cantidad mínima de venta' minSalesQuantity: 'Cantidad mínima de venta'
genus: 'Genus' genus: 'Genus'
specie: 'Specie' specie: 'Specie'
item/itemType:
pageTitles:
itemType: Item type
basicData: Basic data
summary: Summary
monitor:
pageTitles:
monitors: Monitors
list: List
zone:
pageTitles:
zones: Zones
zonesList: Zones
deliveryList: Delivery days
upcomingList: Upcoming deliveries
components: components:
topbar: {} topbar: {}
itemsFilterPanel: itemsFilterPanel:

View File

@ -100,14 +100,144 @@ globals:
modes: Modos modes: Modos
zones: Zonas zones: Zonas
zonesList: Zonas zonesList: Zonas
deliveryList: Días de entrega deliveryDays: Días de entrega
upcomingList: Próximos repartos upcomingDeliveries: Próximos repartos
role: Role role: Role
alias: Alias alias: Alias
aliasUsers: Usuarios aliasUsers: Usuarios
subRoles: Subroles subRoles: Subroles
inheritedRoles: Roles heredados inheritedRoles: Roles heredados
customers: Clientes
customerCreate: Nuevo cliente
list: Listado
webPayments: Pagos Web
extendedList: Listado extendido
notifications: Notificaciones
defaulter: Morosos
createCustomer: Crear cliente
fiscalData: Datos fiscales
billingData: Forma de pago
consignees: Consignatarios
notes: Notas
credits: Créditos
greuges: Greuges
balance: Balance
recoveries: Recobros
webAccess: Acceso web
sms: Sms
creditManagement: Gestión de crédito
creditContracts: Contratos de crédito
creditOpinion: Opinión de crédito
others: Otros
samples: Plantillas
consumption: Consumo
mandates: Mandatos
contacts: Contactos
webPayment: Pago web
fileManagement: Gestión documental
unpaid: Impago
entries: Entradas
buys: Compras
dms: Gestión documental
entryCreate: Nueva entrada
latestBuys: Últimas compras
tickets: Tickets
ticketCreate: Nuevo ticket
boxing: Encajado
sale: Lineas del pedido
claims: Reclamaciones
claimCreate: Crear reclamación
lines: Líneas
development: Trazabilidad
photos: Fotos
action: Acción
invoiceOuts: Fact. emitidas
negativeBases: Bases Negativas
globalInvoicing: Facturación global
invoiceOutCreate: Crear fact. emitida
order: Cesta
orderList: Listado
orderCreate: Nueva orden
catalog: Catálogo
volume: Volumen
shelving: Carros
shelvingList: Listado de carros
shelvingCreate: Nuevo carro
invoiceIns: Fact. recibidas
invoiceInCreate: Crear fact. recibida
vat: IVA
labeler: Etiquetas
dueDay: Vencimiento
intrastat: Intrastat
corrective: Rectificativa
workers: Trabajadores workers: Trabajadores
workerCreate: Nuevo trabajador
department: Departamentos
pda: PDA
pbx: Centralita
calendar: Calendario
timeControl: Control de horario
locker: Taquilla
wagons: Vagones
wagonsList: Listado vagones
wagonCreate: Crear tipo
wagonEdit: Editar tipo
typesList: Listado tipos
typeCreate: Crear tipo
typeEdit: Editar tipo
wagonCounter: Contador de carros
roadmap: Troncales
stops: Paradas
routes: Rutas
cmrsList: Listado de CMRs
RouteList: Listado
routeCreate: Nueva ruta
RouteRoadmap: Troncales
RouteRoadmapCreate: Crear troncal
autonomous: Autónomos
suppliers: Proveedores
supplier: Proveedor
supplierCreate: Nuevo proveedor
accounts: Cuentas
addresses: Direcciones
agencyTerm: Acuerdo agencia
travel: Envíos
create: Crear
extraCommunity: Extra comunitarios
travelCreate: Nuevo envío
history: Historial
thermographs: Termógrafos
items: Artículos
diary: Histórico
tags: Etiquetas
fixedPrice: Precios fijados
buyRequest: Peticiones de compra
wasteBreakdown: Deglose de mermas
itemCreate: Nuevo artículo
tax: 'IVA'
botanical: 'Botánico'
barcode: 'Código de barras'
itemTypeCreate: Nueva familia
family: Familia
lastEntries: Últimas entradas
itemType: Familia
monitors: Monitores
dashboard: Tablón
users: Usuarios
createTicket: Crear ticket
ticketAdvance: Adelantar tickets
futureTickets: Tickets a futuro
purchaseRequest: Petición de compra
weeklyTickets: Tickets programados
formation: Formación
locations: Ubicaciones
warehouses: Almacenes
roles: Roles
connections: Conexiones
acls: ACLs
mailForwarding: Reenvío de correo
mailAlias: Alias de correo
privileges: Privilegios
created: Fecha creación created: Fecha creación
worker: Trabajador worker: Trabajador
now: Ahora now: Ahora
@ -147,41 +277,8 @@ verifyEmail:
verifyEmail: Verificación de correo verifyEmail: Verificación de correo
dashboard: dashboard:
pageTitles: pageTitles:
dashboard: Tablón
customer: customer:
pageTitles:
customers: Clientes
customerCreate: Nuevo cliente
list: Listado
webPayments: Pagos Web
extendedList: Listado extendido
notifications: Notificaciones
defaulter: Morosos
createCustomer: Crear cliente
summary: Resumen
basicData: Datos básicos
fiscalData: Datos fiscales
billingData: Forma de pago
consignees: Consignatarios
notes: Notas
credits: Créditos
greuges: Greuges
balance: Balance
recoveries: Recobros
webAccess: Acceso web
log: Historial
sms: Sms
creditManagement: Gestión de crédito
creditContracts: Contratos de crédito
creditOpinion: Opinión de crédito
others: Otros
samples: Plantillas
consumption: Consumo
mandates: Mandatos
contacts: Contactos
webPayment: Pago web
fileManagement: Gestión documental
unpaid: Impago
list: list:
phone: Teléfono phone: Teléfono
email: Email email: Email
@ -310,17 +407,6 @@ customer:
hasCoreVnl: Recibido core VNL hasCoreVnl: Recibido core VNL
hasSepaVnl: Recibido B2B VNL hasSepaVnl: Recibido B2B VNL
entry: entry:
pageTitles:
entries: Entradas
list: Listado
summary: Resumen
basicData: Datos básicos
buys: Compras
notes: Notas
dms: Gestión documental
log: Historial
entryCreate: Nueva entrada
latestBuys: Últimas compras
list: list:
newEntry: Nueva entrada newEntry: Nueva entrada
landed: F. entrega landed: F. entrega
@ -443,10 +529,14 @@ ticket:
sms: Sms sms: Sms
notes: Notas notes: Notas
sale: Lineas del pedido sale: Lineas del pedido
volume: Volumen
observation: Notas
ticketAdvance: Adelantar tickets ticketAdvance: Adelantar tickets
futureTickets: Tickets a futuro futureTickets: Tickets a futuro
purchaseRequest: Petición de compra purchaseRequest: Petición de compra
weeklyTickets: Tickets programados weeklyTickets: Tickets programados
services: Servicios
tracking: Estados
list: list:
nickname: Alias nickname: Alias
state: Estado state: Estado
@ -521,18 +611,6 @@ ticket:
warehouse: Almacén warehouse: Almacén
agency: Agencia agency: Agencia
claim: claim:
pageTitles:
claims: Reclamaciones
list: Listado
claimCreate: Crear reclamación
summary: Resumen
basicData: Datos básicos
lines: Líneas
development: Trazabilidad
photos: Fotos
log: Historial
notes: Notas
action: Acción
list: list:
customer: Cliente customer: Cliente
assignedTo: Asignada a assignedTo: Asignada a
@ -596,14 +674,6 @@ claim:
noData: No hay imágenes/videos haz click aquí o arrastra y suelta el archivo noData: No hay imágenes/videos haz click aquí o arrastra y suelta el archivo
dragDrop: Arrástralo y sueltalo aquí dragDrop: Arrástralo y sueltalo aquí
invoiceOut: invoiceOut:
pageTitles:
invoiceOuts: Fact. emitidas
list: Listado
negativeBases: Bases Negativas
globalInvoicing: Facturación global
invoiceOutCreate: Crear fact. emitida
summary: Resumen
basicData: Datos básicos
list: list:
ref: Referencia ref: Referencia
issued: Fecha emisión issued: Fecha emisión
@ -671,15 +741,6 @@ invoiceOut:
errors: errors:
downloadCsvFailed: Error al descargar CSV downloadCsvFailed: Error al descargar CSV
order: order:
pageTitles:
order: Cesta
orderList: Listado
orderCreate: Nueva orden
summary: Resumen
basicData: Datos básicos
catalog: Catálogo
volume: Volumen
lines: Líneas
field: field:
salesPersonFk: Comercial salesPersonFk: Comercial
clientFk: Cliente clientFk: Cliente
@ -721,13 +782,6 @@ order:
price: Precio price: Precio
amount: Monto amount: Monto
shelving: shelving:
pageTitles:
shelving: Carros
shelvingList: Listado de carros
shelvingCreate: Nuevo carro
summary: Resumen
basicData: Datos básicos
log: Historial
list: list:
parking: Parking parking: Parking
priority: Prioridad priority: Prioridad
@ -753,17 +807,6 @@ parking:
info: Puedes buscar por código de parking info: Puedes buscar por código de parking
label: Buscar parking... label: Buscar parking...
invoiceIn: invoiceIn:
pageTitles:
invoiceIns: Fact. recibidas
list: Listado
invoiceInCreate: Crear fact. recibida
summary: Resumen
basicData: Datos básicos
vat: IVA
dueDay: Vencimiento
intrastat: Intrastat
corrective: Rectificativa
log: Historial
list: list:
ref: Referencia ref: Referencia
supplier: Proveedor supplier: Proveedor
@ -846,7 +889,6 @@ worker:
timeControl: Control de horario timeControl: Control de horario
locker: Taquilla locker: Taquilla
balance: Balance balance: Balance
formation: Formación
list: list:
name: Nombre name: Nombre
email: Email email: Email
@ -926,15 +968,6 @@ worker:
credit: Haber credit: Haber
concept: Concepto concept: Concepto
wagon: wagon:
pageTitles:
wagons: Vagones
wagonsList: Listado vagones
wagonCreate: Crear tipo
wagonEdit: Editar tipo
typesList: Listado tipos
typeCreate: Crear tipo
typeEdit: Editar tipo
wagonCounter: Contador de carros
type: type:
name: Nombre name: Nombre
submit: Guardar submit: Guardar
@ -963,31 +996,7 @@ wagon:
minHeightBetweenTrays: 'La distancia mínima entre bandejas es ' minHeightBetweenTrays: 'La distancia mínima entre bandejas es '
maxWagonHeight: 'La altura máxima del vagón es ' maxWagonHeight: 'La altura máxima del vagón es '
uncompleteTrays: Hay bandejas sin completar uncompleteTrays: Hay bandejas sin completar
route/roadmap:
pageTitles:
roadmap: Troncales
summary: Resumen
basicData: Datos básicos
stops: Paradas
roadmap:
pageTitles:
roadmap: Troncales
summary: Resumen
basicData: Datos básicos
stops: Paradas
route: route:
pageTitles:
routes: Rutas
cmrsList: Listado de CMRs
RouteList: Listado
routeCreate: Nueva ruta
basicData: Datos básicos
summary: Resumen
RouteRoadmap: Troncales
RouteRoadmapCreate: Crear troncal
tickets: Tickets
log: Historial
autonomous: Autónomos
cmr: cmr:
list: list:
results: resultados results: resultados
@ -1015,22 +1024,6 @@ route:
volume: Volumen volume: Volumen
finished: Finalizada finished: Finalizada
supplier: supplier:
pageTitles:
suppliers: Proveedores
supplier: Proveedor
list: Listado
supplierCreate: Nuevo proveedor
summary: Resumen
basicData: Datos básicos
fiscalData: Datos fiscales
billingData: Forma de pago
log: Historial
accounts: Cuentas
contacts: Contactos
addresses: Direcciones
consumption: Consumo
agencyTerm: Acuerdo agencia
dms: Gestión documental
list: list:
payMethod: Método de pago payMethod: Método de pago
payDeadline: Plazo de pago payDeadline: Plazo de pago
@ -1122,16 +1115,6 @@ supplier:
date: Fecha date: Fecha
reference: Referencia reference: Referencia
travel: travel:
pageTitles:
travel: Envíos
list: Listado
create: Crear
summary: Resumen
extraCommunity: Extra comunitarios
travelCreate: Nuevo envío
basicData: Datos básicos
history: Historial
thermographs: Termógrafos
summary: summary:
confirmed: Confirmado confirmed: Confirmado
entryId: Id entrada entryId: Id entrada
@ -1178,24 +1161,6 @@ travel:
travelFileDescription: 'Id envío { travelId }' travelFileDescription: 'Id envío { travelId }'
file: Fichero file: Fichero
item: item:
pageTitles:
items: Artículos
list: Listado
diary: Histórico
tags: Etiquetas
fixedPrice: Precios fijados
buyRequest: Peticiones de compra
wasteBreakdown: Deglose de mermas
itemCreate: Nuevo artículo
basicData: 'Datos básicos'
tax: 'IVA'
botanical: 'Botánico'
barcode: 'Código de barras'
log: Historial
shelving: Carros
itemTypeCreate: Nueva familia
family: Familia
lastEntries: Últimas entradas
descriptor: descriptor:
item: Artículo item: Artículo
buyer: Comprador buyer: Comprador
@ -1281,27 +1246,6 @@ item:
achieved: 'Conseguido' achieved: 'Conseguido'
concept: 'Concepto' concept: 'Concepto'
state: 'Estado' state: 'Estado'
item/itemType:
pageTitles:
itemType: Familia
basicData: Datos básicos
summary: Resumen
zone:
pageTitles:
zones: Zonas
list: Zonas
deliveryList: Días de entrega
upcomingList: Próximos repartos
role:
pageTitles:
zones: Zonas
list: Zonas
deliveryList: Días de entrega
upcomingList: Próximos repartos
monitor:
pageTitles:
monitors: Monitores
list: Listado
components: components:
topbar: {} topbar: {}
itemsFilterPanel: itemsFilterPanel:

View File

@ -224,7 +224,7 @@ async function changeState(value) {
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle <VnTitle
:url="`#/claim/${entityId}/basic-data`" :url="`#/claim/${entityId}/basic-data`"
:text="t('claim.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
<VnLv <VnLv
:label="t('claim.summary.created')" :label="t('claim.summary.created')"

View File

@ -65,6 +65,8 @@ const columns = computed(() => [
url: 'Workers/activeWithInheritedRole', url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'], fields: ['id', 'name'],
where: { role: 'salesPerson' }, where: { role: 'salesPerson' },
optionFilter: 'firstName',
useLike: false,
}, },
create: true, create: true,
columnField: { columnField: {

View File

@ -0,0 +1,120 @@
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { QBtn } from 'quasar';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import { usePrintService } from 'composables/usePrintService';
const { openReport } = usePrintService();
const route = useRoute();
const { t } = useI18n();
const $props = defineProps({
id: {
type: String,
required: false,
default: null,
},
});
const entityId = computed(() => $props.id || route.params.id);
const entriesTableColumns = computed(() => [
{
align: 'left',
name: 'itemFk',
field: 'itemFk',
label: t('globals.id'),
},
{
align: 'left',
name: 'item',
label: t('entry.summary.item'),
field: (row) => row.item.name,
},
{
align: 'left',
name: 'packagingFk',
label: t('entry.summary.package'),
field: 'packagingFk',
},
{
align: 'left',
name: 'stickers',
label: t('entry.summary.stickers'),
field: 'stickers',
},
{
align: 'left',
name: 'packing',
label: t('entry.summary.packing'),
field: 'packing',
},
{
align: 'left',
name: 'grouping',
label: t('entry.summary.grouping'),
field: 'grouping',
},
]);
</script>
<template>
<QDialog ref="dialogRef">
<QCard style="min-width: 800px">
<QCardSection class="row items-center q-pb-none">
<QAvatar
:icon="icon"
color="primary"
text-color="white"
size="xl"
v-if="icon"
/>
<span class="text-h6 text-grey">{{ title }}</span>
<QSpace />
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
</QCardSection>
<QCardActions align="right">
<QBtn
:label="t('Print buys')"
color="primary"
icon="print"
:loading="isLoading"
@click="openReport(`Entries/${entityId}/buy-label`)"
unelevated
autofocus
/>
</QCardActions>
<QCardSection class="row items-center">
<VnPaginate
ref="entryBuysPaginateRef"
:limit="0"
data-key="EntryBuys"
:url="`Entries/${entityId}/getBuys`"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="entriesTableColumns"
row-key="id"
flat
dense
class="q-ml-lg"
:grid="$q.screen.lt.md"
:no-data-label="t('globals.noResults')"
>
<template #body="props">
<QTr>
<QTd v-for="col in props.cols" :key="col.name">
{{ col.value }}
</QTd>
</QTr>
</template>
</QTable>
</template>
</VnPaginate>
</QCardSection>
</QCard>
</QDialog>
</template>

View File

@ -0,0 +1,106 @@
<script setup>
import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
import { toDate } from 'src/filters/index';
import { useQuasar } from 'quasar';
import EntryBuysTableDialog from './EntryBuysTableDialog.vue';
import VnTable from 'components/VnTable/VnTable.vue';
const stateStore = useStateStore();
const { t } = useI18n();
const quasar = useQuasar();
onMounted(async () => {
stateStore.rightDrawer = true;
});
const columns = computed(() => [
{
align: 'left',
name: 'id',
label: t('customer.extendedList.tableVisibleColumns.id'),
chip: {
condition: () => true,
},
isId: true,
isTitle: false,
},
{
align: 'left',
label: t('shipped'),
name: 'shipped',
isTitle: false,
create: true,
cardVisible: true,
format: ({ shipped }) => toDate(shipped),
},
{
align: 'left',
label: t('landed'),
name: 'landed',
isTitle: false,
create: true,
cardVisible: false,
format: ({ landed }) => toDate(landed),
},
{
align: 'left',
label: t('globals.wareHouseIn'),
name: 'warehouseInName',
isTitle: false,
cardVisible: true,
create: false,
},
{
align: 'right',
name: 'tableActions',
computed,
actions: [
{
title: t('printBuys'),
icon: 'print',
action: (row) => printBuys(row.id),
},
],
},
]);
const printBuys = (rowId) => {
quasar.dialog({
component: EntryBuysTableDialog,
componentProps: {
id: rowId,
},
});
};
</script>
<template>
<VnSearchbar
data-key="EntryList"
url="Entries/filter"
:label="t('Search entries')"
:info="t('You can search by entry reference')"
/>
<QPage class="column items-center q-pa-md">
<div class="vn-card-list">
<VnTable
ref="myEntriesRef"
data-key="myEntriesList"
url="Entries/filter"
:order="['landed DESC', 'id DESC']"
:columns="columns"
default-mode="card"
auto-load
>
</VnTable>
</div>
</QPage>
</template>
<i18n>
es:
Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada
</i18n>

View File

@ -8,3 +8,4 @@ entryFilter:
reference: Reference reference: Reference
landed: Landed landed: Landed
shipped: Shipped shipped: Shipped
printBuys: Print buys

View File

@ -1,5 +1,6 @@
Search entries: Buscar entradas Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada You can search by entry reference: Puedes buscar por referencia de la entrada
entryList: entryList:
list: list:
inventoryEntry: Es inventario inventoryEntry: Es inventario
@ -11,3 +12,4 @@ entryFilter:
landed: F. llegada landed: F. llegada
shipped: F. salida shipped: F. salida
Print buys: Imprimir etiquetas

View File

@ -209,7 +209,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
@ -240,7 +240,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
@ -265,7 +265,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
@ -289,7 +289,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">

View File

@ -51,7 +51,7 @@ const filter = {
class="header header-link" class="header header-link"
:to="{ name: 'ShelvingBasicData', params: { id: entityId } }" :to="{ name: 'ShelvingBasicData', params: { id: entityId } }"
> >
{{ t('shelving.pageTitles.basicData') }} {{ t('globals.pageTitles.basicData') }}
<QIcon name="open_in_new" /> <QIcon name="open_in_new" />
</RouterLink> </RouterLink>
<VnLv :label="t('shelving.summary.code')" :value="entity.code" /> <VnLv :label="t('shelving.summary.code')" :value="entity.code" />

View File

@ -0,0 +1,49 @@
<script setup>
import { reactive, ref, onMounted, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import VnInput from 'src/components/common/VnInput.vue';
import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
const { t } = useI18n();
const emit = defineEmits(['onDataSaved']);
const nameInputRef = ref(null);
const serviceFormData = reactive({});
const onDataSaved = (formData, requestResponse) => {
emit('onDataSaved', formData, requestResponse);
};
onMounted(async () => {
await nextTick();
nameInputRef.value.focus();
});
</script>
<template>
<FormModelPopup
url-create="TicketServiceTypes"
model="TicketServiceType"
:title="t('New service type')"
:form-initial-data="serviceFormData"
@on-data-saved="onDataSaved"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
ref="nameInputRef"
:label="t('service.description')"
v-model="data.name"
:required="true"
/>
</VnRow>
</template>
</FormModelPopup>
</template>
<i18n>
es:
New service type: Nuevo tipo de servicio
</i18n>

View File

@ -0,0 +1,86 @@
<script setup>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import { useState } from 'src/composables/useState';
const emit = defineEmits(['onRequestCreated']);
const route = useRoute();
const { t } = useI18n();
const state = useState();
const user = state.getUser();
const stateFetchDataRef = ref(null);
const statesOptions = ref([]);
const workersOptions = ref([]);
const onStateFkChange = (formData) => (formData.userFk = user.value.id);
</script>
<template>
<FetchData
ref="stateFetchDataRef"
url="States"
auto-load
@on-fetch="(data) => (statesOptions = data)"
/>
<FetchData
url="Workers/search"
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
auto-load
@on-fetch="(data) => (workersOptions = data)"
/>
<FormModelPopup
:title="t('Create tracking')"
url-create="Tickets/state"
model="CreateTicketTracking"
:form-initial-data="{ ticketFk: route.params.id }"
@on-data-saved="() => emit('onRequestCreated')"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
v-model="data.stateFk"
:label="t('tracking.state')"
:options="statesOptions"
@update:model-value="onStateFkChange(data)"
hide-selected
option-label="name"
option-value="id"
/>
<VnSelect
:label="t('tracking.worker')"
v-model="data.userFk"
:options="workersOptions"
hide-selected
option-label="name"
option-value="id"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel>
{{ opt.name }}
</QItemLabel>
<QItemLabel caption>
{{ opt.nickname }}, {{ opt.code }}
</QItemLabel>
</QItemSection>
</QItem>
</template></VnSelect
>
</VnRow>
</template>
</FormModelPopup>
</template>
<i18n>
es:
Create tracking: Crear estado
</i18n>

View File

@ -71,7 +71,7 @@ const filter = {
const data = ref(useCardDescription()); const data = ref(useCardDescription());
const setData = (entity) => const setData = (entity) =>
(data.value = useCardDescription(entity.client.name, entity.id)); (data.value = useCardDescription(entity.client?.name, entity.id));
</script> </script>
<template> <template>
@ -92,7 +92,7 @@ const setData = (entity) =>
<template #value> <template #value>
<span class="link"> <span class="link">
{{ entity.clientFk }} {{ entity.clientFk }}
<CustomerDescriptorProxy :id="entity.client.id" /> <CustomerDescriptorProxy :id="entity.client?.id" />
</span> </span>
</template> </template>
</VnLv> </VnLv>
@ -109,8 +109,8 @@ const setData = (entity) =>
<VnLv :label="t('ticket.summary.salesPerson')"> <VnLv :label="t('ticket.summary.salesPerson')">
<template #value> <template #value>
<VnUserLink <VnUserLink
:name="entity.client.salesPersonUser?.name" :name="entity.client?.salesPersonUser?.name"
:worker-id="entity.client.salesPersonFk" :worker-id="entity.client?.salesPersonFk"
/> />
</template> </template>
</VnLv> </VnLv>

View File

@ -0,0 +1,106 @@
<script setup>
import { ref, watch, computed, reactive } 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';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import { useArrayData } from 'src/composables/useArrayData';
const route = useRoute();
const { t } = useI18n();
const ticketNotesCrudRef = ref(null);
const observationTypes = ref([]);
const arrayData = useArrayData('TicketNotes');
const { store } = arrayData;
const crudModelFilter = reactive({
where: { ticketFk: route.params.id },
fields: ['id', 'ticketFk', 'observationTypeFk', 'description'],
});
const crudModelRequiredData = computed(() => ({ ticketFk: route.params.id }));
watch(
() => route.params.id,
async () => {
crudModelFilter.where.ticketFk = route.params.id;
store.filter = crudModelFilter;
await ticketNotesCrudRef.value.reload();
}
);
</script>
<template>
<FetchData
@on-fetch="(data) => (observationTypes = data)"
auto-load
url="ObservationTypes"
/>
<div class="flex justify-center">
<CrudModel
ref="ticketNotesCrudRef"
data-key="TicketNotes"
url="TicketObservations"
model="TicketNotes"
:filter="crudModelFilter"
:data-required="crudModelRequiredData"
:default-remove="false"
auto-load
style="max-width: 800px"
>
<template #body="{ rows }">
<QCard class="q-px-lg q-py-md">
<div
v-for="(row, index) in rows"
:key="index"
class="q-mb-md row items-center q-gutter-x-md"
>
<VnSelect
:label="t('ticketNotes.observationType')"
:options="observationTypes"
hide-selected
option-label="description"
option-value="id"
v-model="row.observationTypeFk"
:disable="!!row.id"
/>
<VnInput
:label="t('ticketNotes.description')"
v-model="row.description"
class="col"
/>
<QIcon
name="delete"
size="sm"
class="cursor-pointer"
color="primary"
@click="ticketNotesCrudRef.remove([row])"
>
<QTooltip>
{{ t('ticketNotes.removeNote') }}
</QTooltip>
</QIcon>
</div>
<VnRow v-if="observationTypes.length > rows.length">
<QIcon
name="add_circle"
class="fill-icon-on-hover q-ml-md"
size="sm"
color="primary"
@click="ticketNotesCrudRef.insert()"
>
<QTooltip>
{{ t('ticketNotes.addNote') }}
</QTooltip>
</QIcon>
</VnRow>
</QCard>
</template>
</CrudModel>
</div>
</template>

View File

@ -0,0 +1,190 @@
<script setup>
import { ref, watch, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CrudModel from 'components/CrudModel.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import FetchData from 'components/FetchData.vue';
import TicketCreateServiceType from './TicketCreateServiceType.vue';
import VnInput from 'src/components/common/VnInput.vue';
import { useArrayData } from 'src/composables/useArrayData';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const ticketServiceTypeFetchRef = ref(null);
const ticketServiceCrudRef = ref(null);
const ticketServiceOptions = ref([]);
const arrayData = useArrayData('TicketNotes');
const { store } = arrayData;
const { notify } = useNotify();
const selected = ref([]);
const defaultTaxClass = ref(null);
const crudModelFilter = computed(() => ({
where: { ticketFk: route.params.id },
}));
const crudModelRequiredData = computed(() => ({
ticketFk: route.params.id,
taxClassFk: defaultTaxClass.value?.id,
}));
watch(
() => route.params.id,
async () => {
store.filter = crudModelFilter.value;
await ticketServiceCrudRef.value.reload();
}
);
onMounted(async () => await getDefaultTaxClass());
const createRefund = async () => {
try {
if (!selected.value.length) return;
const params = {
servicesIds: selected.value.map((s) => +s.ticketFk),
withWarehouse: false,
negative: true,
};
const { data } = await axios.post('Sales/clone', params);
const [refundTicket] = data;
notify(
t('service.createRefundSuccess', {
ticketId: refundTicket.id,
}),
'positive'
);
router.push({ name: 'TicketSale', params: { id: refundTicket.id } });
} catch (error) {
console.error(error);
}
};
const getDefaultTaxClass = async () => {
try {
let filter = {
where: { code: 'G' },
};
const { data } = await axios.get('TaxClasses/findOne', {
params: { filter: JSON.stringify(filter) },
});
defaultTaxClass.value = data;
console.log('defaultTaxClass', defaultTaxClass.value);
} catch (error) {
console.error(error);
}
};
const columns = computed(() => [
{
name: 'description',
label: t('service.description'),
field: (row) => row.ticketServiceTypeFk,
sortable: true,
align: 'left',
},
{
name: 'quantity',
label: t('service.quantity'),
field: (row) => row.quantity,
sortable: true,
align: 'left',
},
{
name: 'price',
label: t('service.price'),
field: (row) => row.price,
sortable: true,
align: 'left',
},
]);
</script>
<template>
<FetchData
ref="ticketServiceTypeFetchRef"
@on-fetch="(data) => (ticketServiceOptions = data)"
auto-load
url="TicketServiceTypes"
/>
<CrudModel
ref="ticketServiceCrudRef"
data-key="TicketService"
url="TicketServices"
model="TicketService"
:filter="crudModelFilter"
:data-required="crudModelRequiredData"
auto-load
v-model:selected="selected"
>
<template #moreBeforeActions>
<QBtn
color="primary"
:label="t('service.pay')"
:disabled="!selected.length"
@click.stop="createRefund()"
/>
</template>
<template #body="{ rows }">
<QTable
:columns="columns"
:rows="rows"
row-key="$index"
selection="multiple"
v-model:selected="selected"
table-header-class="text-left"
>
<template #body-cell-description="{ row, col }">
<QTd auto-width>
<VnSelectDialog
:label="col.label"
v-model="row.ticketServiceTypeFk"
:options="ticketServiceOptions"
option-label="name"
option-value="id"
hide-selected
>
<template #form>
<TicketCreateServiceType
@on-data-saved="ticketServiceTypeFetchRef.fetch()"
/>
</template>
</VnSelectDialog>
</QTd>
</template>
<template #body-cell-quantity="{ row, col }">
<QTd auto-width>
<VnInput
:label="col.label"
v-model.number="row.quantity"
type="number"
min="0"
:info="t('service.quantityInfo')"
/>
</QTd>
</template>
<template #body-cell-price="{ row, col }">
<QTd auto-width>
<VnInput
:label="col.label"
v-model.number="row.price"
type="number"
min="0"
/>
</QTd>
</template>
</QTable>
</template>
</CrudModel>
<QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn fab color="primary" icon="add" @click="ticketServiceCrudRef.insert()" />
</QPageSticky>
</template>

View File

@ -0,0 +1,121 @@
<script setup>
import { ref, computed, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import TicketCreateTracking from './TicketCreateTracking.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import { toDateTimeFormat } from 'src/filters/date.js';
const route = useRoute();
const { t } = useI18n();
const createTrackingDialogRef = ref(null);
const paginateRef = ref(null);
watch(
() => route.params.id,
async (val) => {
paginateFilter.where.ticketFk = val;
paginateRef.value.fetch();
}
);
const paginateFilter = reactive({
include: [
{
relation: 'user',
scope: {
fields: ['id', 'name'],
include: {
relation: 'worker',
scope: {
fields: ['id'],
},
},
},
},
{
relation: 'state',
scope: {
fields: ['name'],
},
},
],
order: ['created DESC'],
where: {
ticketFk: route.params.id,
},
});
const columns = computed(() => [
{
label: t('tracking.state'),
name: 'state',
field: 'state',
align: 'left',
format: (val) => val.name,
},
{
label: t('tracking.worker'),
name: 'worker',
align: 'left',
},
{
label: t('tracking.created'),
name: 'created',
field: 'created',
align: 'left',
format: (val) => toDateTimeFormat(val),
},
]);
const openCreateModal = () => createTrackingDialogRef.value.show();
</script>
<template>
<QPage class="column items-center q-pa-md">
<VnPaginate
ref="paginateRef"
data-key="TicketTracking"
:filter="paginateFilter"
url="TicketTrackings"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
>
<template #body-cell-worker="{ row }">
<QTd @click.stop>
<QBtn flat color="primary">
{{ row.user?.name }}
<WorkerDescriptorProxy :id="row.user?.worker?.id" />
</QBtn>
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
<QDialog
ref="createTrackingDialogRef"
transition-show="scale"
transition-hide="scale"
>
<TicketCreateTracking @on-request-created="paginateRef.fetch()" />
</QDialog>
<QPageSticky :offset="[20, 20]">
<QBtn @click="openCreateModal()" color="primary" fab icon="add" />
<QTooltip class="text-no-wrap">
{{ t('tracking.addState') }}
</QTooltip>
</QPageSticky>
</QPage>
</template>

View File

@ -0,0 +1,153 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import FetchData from 'components/FetchData.vue';
import { useStateStore } from 'stores/useStateStore';
import { dashIfEmpty } from 'src/filters';
import axios from 'axios';
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
const salesRef = ref(null);
watch(
() => route.params.id,
async () => {
await nextTick();
salesRef.value?.fetch();
}
);
const salesFilter = computed(() => ({
include: { relation: 'item' },
order: 'concept',
where: { ticketFk: route.params.id },
limit: 20,
}));
const sales = ref([]);
const packingTypeVolume = ref([]);
const rows = computed(() => sales.value);
const columns = computed(() => [
{
label: t('volume.item'),
name: 'item',
align: 'left',
},
{
label: t('volume.description'),
name: 'description',
align: 'left',
},
{
label: t('volume.packingType'),
name: 'quantity',
field: (row) => row.item.itemPackingTypeFk,
align: 'left',
format: (val) => dashIfEmpty(val),
},
{
label: t('volume.quantity'),
name: 'quantity',
field: 'quantity',
align: 'left',
},
{
label: t('volume.volumeQuantity'),
name: 'quantity',
field: (row) => row.saleVolume?.volume,
align: 'left',
},
]);
const applyVolumes = async (salesData) => {
try {
if (!salesData.length) return;
sales.value = salesData;
const ticket = sales.value[0].ticketFk;
const { data } = await axios.get(`Tickets/${ticket}/getVolume`);
const volumes = new Map(data.saleVolume.map((volume) => [volume.saleFk, volume]));
sales.value.forEach((sale) => {
sale.saleVolume = volumes.get(sale.id);
});
packingTypeVolume.value = data.packingTypeVolume;
} catch (error) {
console.error(error);
}
};
onMounted(() => {
stateStore.rightDrawer = true;
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<FetchData
ref="salesRef"
url="sales"
:filter="salesFilter"
@on-fetch="(data) => applyVolumes(data)"
auto-load
/>
<RightMenu v-if="packingTypeVolume.length">
<template #right-panel>
<QCard
v-for="(packingType, index) in packingTypeVolume"
:key="index"
class="q-pa-md q-mb-md q-ma-md color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection class="column items-center" horizontal>
<span>
{{ t('volume.type') }}:
{{ dashIfEmpty(packingType.description) }}
</span>
</QCardSection>
<QCardSection class="column items-center" horizontal>
<span> {{ t('volume.volume') }}: {{ packingType.volume }} </span>
</QCardSection>
</QCard>
</template>
</RightMenu>
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
>
<template #body-cell-item="{ row }">
<QTd>
<QBtn flat color="primary">
{{ row.itemFk }}
<ItemDescriptorProxy :id="row.itemFk" />
</QBtn>
</QTd>
</template>
<template #body-cell-description="{ row }">
<QTd>
<div class="column">
<span>{{ row.item.name }}</span>
<span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" :max-length="6" />
</div>
</QTd>
</template>
</QTable>
</template>

View File

@ -1,3 +1,19 @@
card:
search: Search tickets
searchInfo: You can search by ticket id or alias
volume:
item: Item
description: Description
packingType: Packing Type
quantity: Quantity
volumeQuantity: m³ per quantity
type: Type
volume: Volume
ticketNotes:
observationType: Observation type
description: Description
removeNote: Remove note
addNote: Add note
ticketSale: ticketSale:
id: Id id: Id
visible: Visible visible: Visible
@ -113,9 +129,6 @@ basicData:
negativesConfirmMessage: Negatives are going to be generated, are you sure you want to advance all the lines? negativesConfirmMessage: Negatives are going to be generated, are you sure you want to advance all the lines?
chooseAnOption: Choose an option chooseAnOption: Choose an option
unroutedTicket: The ticket has been unrouted unroutedTicket: The ticket has been unrouted
card:
search: Search tickets
searchInfo: You can search by ticket id or alias
purchaseRequest: purchaseRequest:
id: Id id: Id
description: Description description: Description
@ -136,3 +149,18 @@ weeklyTickets:
salesperson: Salesperson salesperson: Salesperson
search: Search weekly tickets search: Search weekly tickets
searchInfo: Search weekly tickets by id or client id searchInfo: Search weekly tickets by id or client id
service:
pay: Pay
description: Description
quantity: Quantity
price: Price
removeService: Remove service
newService: New service type
addService: Add service
quantityInfo: To create services with negative amounts mark the service on the source ticket and press the pay button.
createRefundSuccess: 'The following refund ticket have been created: { ticketId }'
tracking:
state: State
worker: Worker
created: Created
addState: Add state

View File

@ -1,6 +1,34 @@
service:
pay: Abonar
description: Descripción
quantity: Cantidad
price: Precio
removeService: Quitar servicio
newService: Nuevo tipo de servicio
addService: Añadir servicio
quantityInfo: Para crear sevicios con cantidades negativas marcar servicio en el ticket origen y apretar el boton abonar.
createRefundSuccess: 'Se ha creado siguiente ticket de abono: { ticketId }'
tracking:
state: Estado
worker: Trabajador
created: Fecha creación
addState: Añadir estado
card: card:
search: Buscar tickets search: Buscar tickets
searchInfo: Buscar tickets por identificador o alias searchInfo: Buscar tickets por identificador o alias
volume:
item: Artículo
description: Descripción
packingType: Encajado
quantity: Cantidad
volumeQuantity: m³ por cantidad
type: Tipo
volume: Volumen
ticketNotes:
observationType: Tipo de observación
description: Descripción
removeNote: Quitar nota
addNote: Añadir nota
purchaseRequest: purchaseRequest:
Id: Id Id: Id
description: Descripción description: Descripción
@ -112,8 +140,6 @@ futureTickets:
moveTicketSuccess: Tickets movidos correctamente moveTicketSuccess: Tickets movidos correctamente
searchInfo: Buscar tickets por fecha searchInfo: Buscar tickets por fecha
futureTicket: Tickets a futuro futureTicket: Tickets a futuro
Search ticket: Buscar tickets
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
ticketSale: ticketSale:
id: Id id: Id
visible: Visible visible: Visible
@ -138,3 +164,5 @@ ticketSale:
shipped: F. Envío shipped: F. Envío
agency: Agencia agency: Agencia
address: Consignatario address: Consignatario
Search ticket: Buscar tickets
You can search by ticket id or alias: Puedes buscar por id o alias del ticket

View File

@ -49,7 +49,7 @@ const entriesTableColumns = computed(() => {
showValue: false, showValue: false,
}, },
{ {
label: t('supplier.pageTitles.supplier'), label: t('globals.pageTitles.supplier'),
field: 'supplierName', field: 'supplierName',
name: 'supplierName', name: 'supplierName',
align: 'left', align: 'left',
@ -248,7 +248,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('travel.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" /> <VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" />
@ -266,7 +266,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('travel.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" /> <VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" />
@ -284,7 +284,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('travel.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" /> <VnLv :label="t('globals.agency')" :value="travel.agency?.name" />

View File

@ -140,7 +140,7 @@ const columns = computed(() => [
sortable: true, sortable: true,
}, },
{ {
label: t('supplier.pageTitles.supplier'), label: t('globals.pageTitles.supplier'),
field: 'cargoSupplierNickname', field: 'cargoSupplierNickname',
name: 'cargoSupplierNickname', name: 'cargoSupplierNickname',
align: 'left', align: 'left',

View File

@ -218,7 +218,7 @@ warehouses();
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelect
:label="t('supplier.pageTitles.supplier')" :label="t('globals.pageTitles.supplier')"
v-model="params.cargoSupplierFk" v-model="params.cargoSupplierFk"
:options="suppliersOptions" :options="suppliersOptions"
option-value="id" option-value="id"

View File

@ -11,7 +11,6 @@ import { useState } from 'src/composables/useState';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { useRole } from 'src/composables/useRole'; import { useRole } from 'src/composables/useRole';
import { useUserConfig } from 'src/composables/useUserConfig'; import { useUserConfig } from 'src/composables/useUserConfig';
import { toLowerCamel } from 'src/filters';
import { useTokenConfig } from 'src/composables/useTokenConfig'; import { useTokenConfig } from 'src/composables/useTokenConfig';
import { useAcl } from 'src/composables/useAcl'; import { useAcl } from 'src/composables/useAcl';
@ -79,13 +78,11 @@ export default route(function (/* { store, ssrContext } */) {
let title = t(`login.title`); let title = t(`login.title`);
const matches = to.matched; const matches = to.matched;
let moduleName;
if (matches && matches.length > 1) { if (matches && matches.length > 1) {
const module = matches[1]; const module = matches[1];
const moduleTitle = module.meta && module.meta.title; const moduleTitle = module.meta && module.meta.title;
moduleName = toLowerCamel(module.name);
if (moduleTitle) { if (moduleTitle) {
title = t(`${moduleName}.pageTitles.${moduleTitle}`); title = t(`globals.pageTitles.${moduleTitle}`);
} }
} }
@ -94,7 +91,7 @@ export default route(function (/* { store, ssrContext } */) {
if (childPageTitle && matches.length > 2) { if (childPageTitle && matches.length > 2) {
if (title != '') title += ': '; if (title != '') title += ': ';
const moduleLocale = `${moduleName}.pageTitles.${childPageTitle}`; const moduleLocale = `globals.pageTitles.${childPageTitle}`;
const pageTitle = te(moduleLocale) const pageTitle = te(moduleLocale)
? t(moduleLocale) ? t(moduleLocale)
: t(`globals.pageTitles.${childPageTitle}`); : t(`globals.pageTitles.${childPageTitle}`);

View File

@ -11,7 +11,7 @@ export default {
component: RouterView, component: RouterView,
redirect: { name: 'EntryMain' }, redirect: { name: 'EntryMain' },
menus: { menus: {
main: ['EntryList', 'EntryLatestBuys'], main: ['EntryList', 'MyEntries', 'EntryLatestBuys'],
card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryDms', 'EntryLog'], card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryDms', 'EntryLog'],
}, },
children: [ children: [
@ -30,6 +30,15 @@ export default {
}, },
component: () => import('src/pages/Entry/EntryList.vue'), component: () => import('src/pages/Entry/EntryList.vue'),
}, },
{
path: 'my',
name: 'MyEntries',
meta: {
title: 'labeler',
icon: 'sell',
},
component: () => import('src/pages/Entry/MyEntries.vue'),
},
{ {
path: 'create', path: 'create',
name: 'EntryCreate', name: 'EntryCreate',

View File

@ -201,15 +201,6 @@ export default {
}, },
component: () => import('src/pages/Item/Card/ItemLog.vue'), component: () => import('src/pages/Item/Card/ItemLog.vue'),
}, },
{
path: 'botanical',
name: 'ItemBotanical',
meta: {
title: 'botanical',
icon: 'vn:botanical',
},
component: () => import('src/pages/Item/Card/ItemBotanical.vue'),
},
], ],
}, },
], ],

View File

@ -19,6 +19,10 @@ export default {
'TicketSale', 'TicketSale',
'TicketLog', 'TicketLog',
'TicketPurchaseRequest', 'TicketPurchaseRequest',
'TicketService',
'TicketTracking',
'TicketVolume',
'TicketNotes',
], ],
}, },
children: [ children: [
@ -29,8 +33,8 @@ export default {
redirect: { name: 'TicketList' }, redirect: { name: 'TicketList' },
children: [ children: [
{ {
name: 'TicketList',
path: 'list', path: 'list',
name: 'TicketList',
meta: { meta: {
title: 'list', title: 'list',
icon: 'view_list', icon: 'view_list',
@ -38,8 +42,8 @@ export default {
component: () => import('src/pages/Ticket/TicketList.vue'), component: () => import('src/pages/Ticket/TicketList.vue'),
}, },
{ {
name: 'TicketCreate',
path: 'create', path: 'create',
name: 'TicketCreate',
meta: { meta: {
title: 'createTicket', title: 'createTicket',
icon: 'vn:ticketAdd', icon: 'vn:ticketAdd',
@ -48,8 +52,8 @@ export default {
component: () => import('src/pages/Ticket/TicketCreate.vue'), component: () => import('src/pages/Ticket/TicketCreate.vue'),
}, },
{ {
name: 'TicketWeekly',
path: 'weekly', path: 'weekly',
name: 'TicketWeekly',
meta: { meta: {
title: 'weeklyTickets', title: 'weeklyTickets',
icon: 'access_time', icon: 'access_time',
@ -57,8 +61,8 @@ export default {
component: () => import('src/pages/Ticket/TicketWeekly.vue'), component: () => import('src/pages/Ticket/TicketWeekly.vue'),
}, },
{ {
name: 'TicketFuture',
path: 'future', path: 'future',
name: 'TicketFuture',
meta: { meta: {
title: 'futureTickets', title: 'futureTickets',
icon: 'keyboard_double_arrow_right', icon: 'keyboard_double_arrow_right',
@ -66,8 +70,8 @@ export default {
component: () => import('src/pages/Ticket/TicketFuture.vue'), component: () => import('src/pages/Ticket/TicketFuture.vue'),
}, },
{ {
name: 'TicketAdvance',
path: 'advance', path: 'advance',
name: 'TicketAdvance',
meta: { meta: {
title: 'ticketAdvance', title: 'ticketAdvance',
icon: 'keyboard_double_arrow_left', icon: 'keyboard_double_arrow_left',
@ -83,8 +87,8 @@ export default {
redirect: { name: 'TicketSummary' }, redirect: { name: 'TicketSummary' },
children: [ children: [
{ {
name: 'TicketSummary',
path: 'summary', path: 'summary',
name: 'TicketSummary',
meta: { meta: {
title: 'summary', title: 'summary',
icon: 'launch', icon: 'launch',
@ -92,8 +96,8 @@ export default {
component: () => import('src/pages/Ticket/Card/TicketSummary.vue'), component: () => import('src/pages/Ticket/Card/TicketSummary.vue'),
}, },
{ {
name: 'TicketBasicData',
path: 'basic-data', path: 'basic-data',
name: 'TicketBasicData',
meta: { meta: {
title: 'basicData', title: 'basicData',
icon: 'vn:settings', icon: 'vn:settings',
@ -102,8 +106,8 @@ export default {
import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'), import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'),
}, },
{ {
name: 'TicketSale',
path: 'sale', path: 'sale',
name: 'TicketSale',
meta: { meta: {
title: 'sale', title: 'sale',
icon: 'vn:lines', icon: 'vn:lines',
@ -120,6 +124,15 @@ export default {
component: () => component: () =>
import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'), import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'),
}, },
{
path: 'tracking',
name: 'TicketTracking',
meta: {
title: 'tracking',
icon: 'vn:eye',
},
component: () => import('src/pages/Ticket/Card/TicketTracking.vue'),
},
{ {
path: 'log', path: 'log',
name: 'TicketLog', name: 'TicketLog',
@ -147,6 +160,34 @@ export default {
}, },
component: () => import('src/pages/Ticket/Card/TicketSms.vue'), component: () => import('src/pages/Ticket/Card/TicketSms.vue'),
}, },
{
path: 'service',
name: 'TicketService',
meta: {
title: 'services',
icon: 'vn:services',
},
component: () => import('src/pages/Ticket/Card/TicketService.vue'),
},
{
path: 'volume',
name: 'TicketVolume',
meta: {
title: 'volume',
icon: 'vn:volume',
},
component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
},
{
path: 'observation',
name: 'TicketNotes',
meta: {
title: 'notes',
icon: 'vn:notes',
},
component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
},
], ],
}, },
], ],

View File

@ -60,7 +60,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
menuChildren = menuChildren.map(({ name, title, icon }) => ({ menuChildren = menuChildren.map(({ name, title, icon }) => ({
name, name,
icon, icon,
title: `${module}.pageTitles.${title}`, title: `globals.pageTitles.${title}`,
})); }));
if (meta && meta.roles && role.hasAny(meta.roles) === false) return; if (meta && meta.roles && role.hasAny(meta.roles) === false) return;
@ -70,7 +70,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
children: menuChildren, children: menuChildren,
}; };
if (meta) { if (meta) {
item.title = `${module}.pageTitles.${meta.title}`; item.title = `globals.pageTitles.${meta.title}`;
item.icon = meta.icon; item.icon = meta.icon;
} }

View File

@ -0,0 +1,20 @@
describe('WagonTypeCreate', () => {
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('customer');
cy.visit(`/#/entry/my`, {
onBeforeLoad(win) {
cy.stub(win, 'open');
},
});
cy.waitForElement('.q-page', 6000);
});
it('should create edit and remove new dms', () => {
cy.get(
'[to="/null/2"] > .q-card > .column > .q-btn > .q-btn__content > .q-icon'
).click();
cy.get('.q-card__actions > .q-btn').click();
cy.window().its('open').should('be.called');
});
});

View File

@ -5,12 +5,11 @@ describe('InvoiceInList', () => {
':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content'; ':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content';
const firstDetailBtn = '.q-card:nth-child(1) .q-btn:nth-child(2)'; const firstDetailBtn = '.q-card:nth-child(1) .q-btn:nth-child(2)';
const summaryHeaders = '.summaryBody .header-link'; const summaryHeaders = '.summaryBody .header-link';
const screen = '.q-page-container > .q-drawer-container > .fullscreen';
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit(`/#/invoice-in/list`); cy.visit(`/#/invoice-in/list`);
cy.get(screen).click();
}); });
it('should redirect on clicking a invoice', () => { it('should redirect on clicking a invoice', () => {

View File

@ -8,9 +8,9 @@ describe('ParkingList', () => {
const summaryHeader = '.summaryBody .header'; const summaryHeader = '.summaryBody .header';
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit(`/#/parking/list`); cy.visit(`/#/parking/list`);
cy.closeSideMenu();
}); });
it('should redirect on clicking a parking', () => { it('should redirect on clicking a parking', () => {

View File

@ -50,8 +50,8 @@ Cypress.Commands.add('login', (user) => {
}); });
}); });
Cypress.Commands.add('waitForElement', (element) => { Cypress.Commands.add('waitForElement', (element, timeout = 5000) => {
cy.get(element, { timeout: 5000 }).should('be.visible'); cy.get(element, { timeout }).should('be.visible');
}); });
Cypress.Commands.add('getValue', (selector) => { Cypress.Commands.add('getValue', (selector) => {
@ -221,10 +221,6 @@ Cypress.Commands.add('openLeftMenu', (element) => {
if (element) cy.waitForElement(element); if (element) cy.waitForElement(element);
cy.get('.q-toolbar > .q-btn--round.q-btn--dense > .q-btn__content > .q-icon').click(); cy.get('.q-toolbar > .q-btn--round.q-btn--dense > .q-btn__content > .q-icon').click();
}); });
Cypress.Commands.add('closeSideMenu', (element) => {
if (element) cy.waitForElement(element);
cy.get('.fullscreen.q-drawer__backdrop:not(.hidden)').click();
});
Cypress.Commands.add('clearSearchbar', (element) => { Cypress.Commands.add('clearSearchbar', (element) => {
if (element) cy.waitForElement(element); if (element) cy.waitForElement(element);
@ -245,4 +241,3 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
Cypress.Commands.add('openActionsDescriptor', () => { Cypress.Commands.add('openActionsDescriptor', () => {
cy.get('.descriptor > .header > .q-btn').click(); cy.get('.descriptor > .header > .q-btn').click();
}); });
// registerCommands();

View File

@ -78,13 +78,13 @@ describe('Leftmenu', () => {
{ {
children: null, children: null,
name: 'CustomerList', name: 'CustomerList',
title: 'customer.pageTitles.list', title: 'globals.pageTitles.list',
icon: 'view_list', icon: 'view_list',
}, },
{ {
children: null, children: null,
name: 'CustomerCreate', name: 'CustomerCreate',
title: 'customer.pageTitles.createCustomer', title: 'globals.pageTitles.createCustomer',
icon: 'vn:addperson', icon: 'vn:addperson',
}, },
]; ];