Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d6978e9c7 | ||
|
|
62ae4bc890 | ||
|
|
c2ce6a859b | ||
|
|
aa5e0a5507 | ||
|
|
6c1c1def78 | ||
|
|
1a232183a8 | ||
|
|
cfa7d5be69 | ||
|
|
bb375eb149 | ||
|
|
7e5e6ce680 | ||
|
|
1032f8b4f1 | ||
|
|
744a56c8b2 | ||
|
|
e58372a190 | ||
|
|
19d44b8666 | ||
|
|
92a52d6194 | ||
|
|
db1c0e25c0 | ||
|
|
04860a7304 | ||
|
|
d8d67301ed | ||
|
|
fa1e915b9c | ||
|
|
da86b69b2c | ||
|
|
44e3167278 | ||
|
|
5cc22f14bc | ||
|
|
8519af5cbb | ||
|
|
3fc4be8335 | ||
|
|
5a5cc0bed3 | ||
|
|
1a9e7503ef | ||
|
|
d84e9e3406 | ||
|
|
c41954993c | ||
|
|
a7c8fc3bff | ||
|
|
36c1f10de8 | ||
|
|
6002f97fb6 | ||
|
|
4378dad2b9 | ||
|
|
278298ecd2 | ||
|
|
b8959cb7bb | ||
|
|
dec90bbbbf | ||
|
|
a4b9d2f04d | ||
|
|
ee41366c08 | ||
|
|
3f42b77d13 | ||
|
|
e22f78a449 | ||
|
|
f2b66ff19f | ||
|
|
2fd0893773 | ||
|
|
0e9292d6d9 | ||
|
|
69250ada66 | ||
|
|
22f8b5f203 | ||
|
|
987acc5b0f | ||
|
|
ce844137a0 | ||
|
|
7f3a8c07e2 | ||
|
|
bd14d58f08 | ||
|
|
b8494cd1d9 | ||
|
|
1a1c3db8e5 | ||
|
|
49772c1334 | ||
|
|
02ddb99000 | ||
|
|
cf1509e34f | ||
|
|
ce2e671768 |
@@ -101,8 +101,8 @@ jobs:
|
|||||||
if: github.event_name != 'schedule'
|
if: github.event_name != 'schedule'
|
||||||
needs: build-and-unit
|
needs: build-and-unit
|
||||||
name: E2E-pr-${{ matrix.suite }}-${{ matrix.project }}
|
name: E2E-pr-${{ matrix.suite }}-${{ matrix.project }}
|
||||||
runs-on: [self-hosted, Linux, X64, pleno, frontend, docker]
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 30
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 4
|
max-parallel: 4
|
||||||
@@ -112,6 +112,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
PLAYWRIGHT_ARTIFACT_NAMESPACE: e2e-pr-${{ matrix.suite }}-${{ matrix.project }}
|
PLAYWRIGHT_ARTIFACT_NAMESPACE: e2e-pr-${{ matrix.suite }}-${{ matrix.project }}
|
||||||
PLAYWRIGHT_REPORTER_MODE: line-html
|
PLAYWRIGHT_REPORTER_MODE: line-html
|
||||||
|
PLAYWRIGHT_WORKERS: 1
|
||||||
|
PLAYWRIGHT_VIDEO_MODE: on-first-retry
|
||||||
steps:
|
steps:
|
||||||
- name: Repair self-hosted workspace permissions
|
- name: Repair self-hosted workspace permissions
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -140,20 +142,29 @@ jobs:
|
|||||||
EVENT_NAME: ${{ github.event_name }}
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
HEAD_SHA: ${{ github.sha }}
|
HEAD_SHA: ${{ github.sha }}
|
||||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
PUSH_BEFORE_SHA: ${{ github.event.before }}
|
PUSH_BEFORE_SHA: ${{ github.event.before }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
zero_sha="0000000000000000000000000000000000000000"
|
zero_sha="0000000000000000000000000000000000000000"
|
||||||
if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_BASE_SHA" ]]; then
|
if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_BASE_SHA" ]]; then
|
||||||
base_ref="$PR_BASE_SHA"
|
base_ref="$PR_BASE_SHA"
|
||||||
|
head_ref="$PR_HEAD_SHA"
|
||||||
elif [[ -z "$PUSH_BEFORE_SHA" || "$PUSH_BEFORE_SHA" == "$zero_sha" ]]; then
|
elif [[ -z "$PUSH_BEFORE_SHA" || "$PUSH_BEFORE_SHA" == "$zero_sha" ]]; then
|
||||||
git fetch --no-tags --prune origin "$DEFAULT_BRANCH"
|
git fetch --no-tags --prune origin "$DEFAULT_BRANCH"
|
||||||
base_ref="origin/$DEFAULT_BRANCH"
|
base_ref="origin/$DEFAULT_BRANCH"
|
||||||
|
head_ref="$HEAD_SHA"
|
||||||
else
|
else
|
||||||
base_ref="$PUSH_BEFORE_SHA"
|
base_ref="$PUSH_BEFORE_SHA"
|
||||||
|
head_ref="$HEAD_SHA"
|
||||||
|
fi
|
||||||
|
if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_HEAD_SHA" ]]; then
|
||||||
|
head_ref="$PR_HEAD_SHA"
|
||||||
|
else
|
||||||
|
head_ref="$HEAD_SHA"
|
||||||
fi
|
fi
|
||||||
echo "base=$base_ref" >> "$GITHUB_OUTPUT"
|
echo "base=$base_ref" >> "$GITHUB_OUTPUT"
|
||||||
echo "head=$HEAD_SHA" >> "$GITHUB_OUTPUT"
|
echo "head=$head_ref" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v5
|
||||||
@@ -181,8 +192,9 @@ jobs:
|
|||||||
*) echo "Unsupported Playwright PR project: $MATRIX_PROJECT" >&2; exit 1 ;;
|
*) echo "Unsupported Playwright PR project: $MATRIX_PROJECT" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
port_seed=$((20000 + (RUN_ID % 20000) + suite_offset + project_offset))
|
port_seed=$((20000 + (RUN_ID % 20000) + suite_offset + project_offset))
|
||||||
lock_root="${RUNNER_TEMP:-/tmp}/pleno-playwright-port-locks"
|
lock_root="${PLAYWRIGHT_PORT_LOCK_ROOT:-/tmp/pleno-playwright-port-locks}"
|
||||||
mkdir -p "$lock_root"
|
mkdir -p "$lock_root"
|
||||||
|
chmod 1777 "$lock_root" 2>/dev/null || true
|
||||||
find "$lock_root" -mindepth 1 -maxdepth 1 -type d -mmin +360 -exec rmdir {} \; 2>/dev/null || true
|
find "$lock_root" -mindepth 1 -maxdepth 1 -type d -mmin +360 -exec rmdir {} \; 2>/dev/null || true
|
||||||
playwright_port_lock=""
|
playwright_port_lock=""
|
||||||
playwright_dev_port=""
|
playwright_dev_port=""
|
||||||
@@ -223,6 +235,8 @@ jobs:
|
|||||||
--env CI="${CI:-}" \
|
--env CI="${CI:-}" \
|
||||||
--env PLAYWRIGHT_ARTIFACT_NAMESPACE="$PLAYWRIGHT_ARTIFACT_NAMESPACE" \
|
--env PLAYWRIGHT_ARTIFACT_NAMESPACE="$PLAYWRIGHT_ARTIFACT_NAMESPACE" \
|
||||||
--env PLAYWRIGHT_REPORTER_MODE="$PLAYWRIGHT_REPORTER_MODE" \
|
--env PLAYWRIGHT_REPORTER_MODE="$PLAYWRIGHT_REPORTER_MODE" \
|
||||||
|
--env PLAYWRIGHT_WORKERS="$PLAYWRIGHT_WORKERS" \
|
||||||
|
--env PLAYWRIGHT_VIDEO_MODE="$PLAYWRIGHT_VIDEO_MODE" \
|
||||||
--env PLAYWRIGHT_DEV_PORT="$playwright_dev_port" \
|
--env PLAYWRIGHT_DEV_PORT="$playwright_dev_port" \
|
||||||
--env MATRIX_SUITE="$MATRIX_SUITE" \
|
--env MATRIX_SUITE="$MATRIX_SUITE" \
|
||||||
--env MATRIX_PROJECT="$MATRIX_PROJECT" \
|
--env MATRIX_PROJECT="$MATRIX_PROJECT" \
|
||||||
@@ -357,8 +371,9 @@ jobs:
|
|||||||
*) echo "Unsupported Playwright device: $MATRIX_DEVICE" >&2; exit 1 ;;
|
*) echo "Unsupported Playwright device: $MATRIX_DEVICE" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
port_seed=$((20000 + (RUN_ID % 20000) + role_offset + browser_offset + device_offset))
|
port_seed=$((20000 + (RUN_ID % 20000) + role_offset + browser_offset + device_offset))
|
||||||
lock_root="${RUNNER_TEMP:-/tmp}/pleno-playwright-port-locks"
|
lock_root="${PLAYWRIGHT_PORT_LOCK_ROOT:-/tmp/pleno-playwright-port-locks}"
|
||||||
mkdir -p "$lock_root"
|
mkdir -p "$lock_root"
|
||||||
|
chmod 1777 "$lock_root" 2>/dev/null || true
|
||||||
find "$lock_root" -mindepth 1 -maxdepth 1 -type d -mmin +360 -exec rmdir {} \; 2>/dev/null || true
|
find "$lock_root" -mindepth 1 -maxdepth 1 -type d -mmin +360 -exec rmdir {} \; 2>/dev/null || true
|
||||||
playwright_port_lock=""
|
playwright_port_lock=""
|
||||||
playwright_dev_port=""
|
playwright_dev_port=""
|
||||||
|
|||||||
@@ -2415,6 +2415,12 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
description: Contact person name
|
description: Contact person name
|
||||||
example: "Mikkel"
|
example: "Mikkel"
|
||||||
|
ean:
|
||||||
|
type: string
|
||||||
|
description: Optional EAN used for e-invoicing in e-conomic
|
||||||
|
maxLength: 13
|
||||||
|
pattern: '^[0-9]{1,13}$'
|
||||||
|
example: "5790001234567"
|
||||||
g_recaptcha_response:
|
g_recaptcha_response:
|
||||||
type: string
|
type: string
|
||||||
description: reCAPTCHA verification token
|
description: reCAPTCHA verification token
|
||||||
@@ -8189,6 +8195,12 @@ paths:
|
|||||||
email: {type: string}
|
email: {type: string}
|
||||||
phone: {type: integer}
|
phone: {type: integer}
|
||||||
name: {type: string}
|
name: {type: string}
|
||||||
|
ean:
|
||||||
|
type: string
|
||||||
|
description: Optional EAN used for e-invoicing in e-conomic
|
||||||
|
maxLength: 13
|
||||||
|
pattern: '^[0-9]{1,13}$'
|
||||||
|
example: "5790001234567"
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Success
|
description: Success
|
||||||
|
|||||||
@@ -7,14 +7,18 @@ export const fallbackChangePatterns = [
|
|||||||
/^vite\.config\.js$/u,
|
/^vite\.config\.js$/u,
|
||||||
/^playwright(?:\..+)?\.config\.(?:js|ts)$/u,
|
/^playwright(?:\..+)?\.config\.(?:js|ts)$/u,
|
||||||
/^playwright\.global-(?:setup|teardown)\.mjs$/u,
|
/^playwright\.global-(?:setup|teardown)\.mjs$/u,
|
||||||
/^scripts\/run-playwright/u,
|
/^scripts\/run-playwright-(?:ci-parallel|batched-chromium)\.mjs$/u,
|
||||||
/^tests\/e2e\/(?:support|fixtures)\//u,
|
/^tests\/e2e\/(?:support|fixtures)\//u,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const sourceMappings = [
|
export const sourceMappings = [
|
||||||
{
|
{
|
||||||
name: "auth",
|
name: "auth",
|
||||||
patterns: [/^src\/(?:views|components|middleware)\/.*auth/iu, /^src\/views\/auth\//u, /^src\/components\/session\//u],
|
patterns: [
|
||||||
|
/^src\/(?:views|components|middleware)\/.*auth/iu,
|
||||||
|
/^src\/views\/auth\//u,
|
||||||
|
/^src\/components\/session\/(?!token\/SessionUser\/Objects\/)/u,
|
||||||
|
],
|
||||||
specs: ["tests/e2e/auth.smoke.spec.js", "tests/e2e/userAuth.spec.ts"],
|
specs: ["tests/e2e/auth.smoke.spec.js", "tests/e2e/userAuth.spec.ts"],
|
||||||
projects: chromiumProjects,
|
projects: chromiumProjects,
|
||||||
},
|
},
|
||||||
@@ -24,6 +28,55 @@ export const sourceMappings = [
|
|||||||
specs: ["tests/e2e/navigation.smoke.spec.js"],
|
specs: ["tests/e2e/navigation.smoke.spec.js"],
|
||||||
projects: chromiumProjects,
|
projects: chromiumProjects,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "backoffice",
|
||||||
|
patterns: [/^src\/views\/backoffice\//u],
|
||||||
|
specs: ["tests/e2e/limited-backoffice.spec.ts"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "superuser-roles-permissions",
|
||||||
|
patterns: [
|
||||||
|
/^src\/views\/dashboards\/superUserDashboard\/roles\/SuperUserRolesPermissions\.vue$/u,
|
||||||
|
/^src\/views\/dashboards\/superUserDashboard\/roles\/rolePermissionCatalog\.js$/u,
|
||||||
|
],
|
||||||
|
specs: ["tests/e2e/superuser-roles-permissions.spec.ts"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "superuser-users",
|
||||||
|
patterns: [/^src\/views\/dashboards\/superUserDashboard\/user\//u],
|
||||||
|
specs: ["tests/e2e/superuser-users.spec.ts"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "superuser-dashboard",
|
||||||
|
patterns: [
|
||||||
|
/^src\/views\/dashboards\/superUserDashboard\/SuperUserDashboard(?:Navigation)?\.vue$/u,
|
||||||
|
/^src\/components\/displays\/superuser\/system\//u,
|
||||||
|
],
|
||||||
|
specs: ["tests/e2e/superuser-system-status.smoke.spec.js"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "superuser-department-overview",
|
||||||
|
patterns: [
|
||||||
|
/^src\/services\/superuserDepartmentOverview\.js$/u,
|
||||||
|
/^src\/views\/dashboards\/superUserDashboard\/department\/Department\.vue$/u,
|
||||||
|
/^src\/views\/dashboards\/superUserDashboard\/department\/SuperUserDashboardDepartmentNavigation\.vue$/u,
|
||||||
|
],
|
||||||
|
specs: ["tests/e2e/superuser-department-overview.spec.js"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "header-navigation",
|
||||||
|
patterns: [
|
||||||
|
/^src\/components\/viewport\/page\/headers\//u,
|
||||||
|
/^src\/components\/models\/navigation\/items\/NavigationMenuItemsGlobal\.vue$/u,
|
||||||
|
],
|
||||||
|
specs: ["tests/e2e/navigation.smoke.spec.js", "tests/e2e/limited-backoffice.spec.ts"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "booking",
|
name: "booking",
|
||||||
patterns: [/bookings?/iu, /time-bookings/iu, /^src\/views\/guest\/book\//u],
|
patterns: [/bookings?/iu, /time-bookings/iu, /^src\/views\/guest\/book\//u],
|
||||||
@@ -42,8 +95,20 @@ export const sourceMappings = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pos",
|
name: "pos",
|
||||||
patterns: [/\/pos[/-]/iu, /POS/iu, /^src\/assets\/pos\.css$/u],
|
patterns: [
|
||||||
specs: ["tests/e2e/pos-flow.spec.js", "tests/e2e/pos-mobile-order-flow.spec.js", "tests/e2e/admin-pos-orders.spec.ts"],
|
/\/pos[/-]/iu,
|
||||||
|
/POS/iu,
|
||||||
|
/^src\/assets\/pos\.css$/u,
|
||||||
|
/^src\/components\/displays\/boxes\/ProductBox\.vue$/u,
|
||||||
|
/^src\/features\/customer\/customerProductRules\.js$/u,
|
||||||
|
/^src\/services\/economicCustomerIdentifiers\.js$/u,
|
||||||
|
],
|
||||||
|
specs: [
|
||||||
|
"tests/e2e/pos-flow.spec.js",
|
||||||
|
"tests/e2e/pos-mobile-order-flow.spec.js",
|
||||||
|
"tests/e2e/admin-pos-orders.spec.ts",
|
||||||
|
"tests/e2e/pos-customer-rules.spec.js",
|
||||||
|
],
|
||||||
projects: chromiumProjects,
|
projects: chromiumProjects,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -52,6 +117,21 @@ export const sourceMappings = [
|
|||||||
specs: ["tests/e2e/edge-gateways.smoke.spec.js", "tests/e2e/edge-gateways.routes.spec.js"],
|
specs: ["tests/e2e/edge-gateways.smoke.spec.js", "tests/e2e/edge-gateways.routes.spec.js"],
|
||||||
projects: chromiumProjects,
|
projects: chromiumProjects,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "admin-department-notifications",
|
||||||
|
patterns: [
|
||||||
|
/^src\/components\/displays\/department\/notifications\//u,
|
||||||
|
/^src\/components\/displays\/pagination\/models\/DepartmentPos\/NotificationsPhonePagination\.vue$/u,
|
||||||
|
],
|
||||||
|
specs: ["tests/e2e/admin-department-notifications.spec.ts"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "limited-backoffice",
|
||||||
|
patterns: [/^src\/views\/backoffice\/LimitedBackoffice/u],
|
||||||
|
specs: ["tests/e2e/limited-backoffice.spec.ts"],
|
||||||
|
projects: chromiumProjects,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "invoicing",
|
name: "invoicing",
|
||||||
patterns: [/invoic/iu, /economic[-/]?queue/iu, /collected-order/iu],
|
patterns: [/invoic/iu, /economic[-/]?queue/iu, /collected-order/iu],
|
||||||
@@ -74,6 +154,15 @@ export const sourceMappings = [
|
|||||||
specs: ["tests/e2e/superuser-system-status.smoke.spec.js"],
|
specs: ["tests/e2e/superuser-system-status.smoke.spec.js"],
|
||||||
projects: chromiumProjects,
|
projects: chromiumProjects,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "superuser-department-pricing",
|
||||||
|
patterns: [
|
||||||
|
/^src\/views\/dashboards\/superUserDashboard\/department\/(?:DepartmentPricing|SuperUserSelectedDepartmentObject)\.vue$/u,
|
||||||
|
/^src\/components\/session\/token\/SessionUser\/Objects\/Departments\.vue$/u,
|
||||||
|
],
|
||||||
|
specs: ["tests/e2e/superuser-department-pricing-custom-only.spec.ts"],
|
||||||
|
projects: ["chromium-desktop"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "self-serve",
|
name: "self-serve",
|
||||||
patterns: [/self[-/]?serve/iu, /selfserve/iu, /wash\/MyWash/iu],
|
patterns: [/self[-/]?serve/iu, /selfserve/iu, /wash\/MyWash/iu],
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export const ownedFilesByRole = {
|
|||||||
admin: [
|
admin: [
|
||||||
"admin-bookings-mobile.spec.ts",
|
"admin-bookings-mobile.spec.ts",
|
||||||
"admin-daily-report.spec.ts",
|
"admin-daily-report.spec.ts",
|
||||||
|
"admin-department-notifications.spec.ts",
|
||||||
"admin-department-visibility.spec.ts",
|
"admin-department-visibility.spec.ts",
|
||||||
"admin-overview-mobile.spec.ts",
|
"admin-overview-mobile.spec.ts",
|
||||||
"admin-overview-night-washes.spec.ts",
|
"admin-overview-night-washes.spec.ts",
|
||||||
@@ -100,11 +101,14 @@ export const ownedFilesByRole = {
|
|||||||
"superuser-customer-complaints.spec.ts",
|
"superuser-customer-complaints.spec.ts",
|
||||||
"superuser-customers-mass-import.spec.ts",
|
"superuser-customers-mass-import.spec.ts",
|
||||||
"superuser-department-branding.spec.js",
|
"superuser-department-branding.spec.js",
|
||||||
|
"superuser-department-overview.spec.js",
|
||||||
"superuser-department-gates.spec.ts",
|
"superuser-department-gates.spec.ts",
|
||||||
"superuser-department-lanes.spec.ts",
|
"superuser-department-lanes.spec.ts",
|
||||||
|
"superuser-department-pricing-custom-only.spec.ts",
|
||||||
"superuser-departments-archive.spec.ts",
|
"superuser-departments-archive.spec.ts",
|
||||||
"superuser-drafts.spec.ts",
|
"superuser-drafts.spec.ts",
|
||||||
"superuser-products-layout.spec.ts",
|
"superuser-products-layout.spec.ts",
|
||||||
|
"superuser-roles-permissions.spec.ts",
|
||||||
"superuser-system-status.smoke.spec.js",
|
"superuser-system-status.smoke.spec.js",
|
||||||
"superuser-users.spec.ts",
|
"superuser-users.spec.ts",
|
||||||
"superuser-vehicles.smoke.spec.js",
|
"superuser-vehicles.smoke.spec.js",
|
||||||
|
|||||||
@@ -261,6 +261,8 @@ function selectChangedTests(changedFiles) {
|
|||||||
specProjects: new Map(),
|
specProjects: new Map(),
|
||||||
mappedFiles: [],
|
mappedFiles: [],
|
||||||
unmappedFiles: [],
|
unmappedFiles: [],
|
||||||
|
directSpecFiles: [],
|
||||||
|
skippedDirectSpecFiles: [],
|
||||||
fallback: false,
|
fallback: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -270,8 +272,7 @@ function selectChangedTests(changedFiles) {
|
|||||||
const file = normalizePath(rawFile);
|
const file = normalizePath(rawFile);
|
||||||
|
|
||||||
if (isE2eSpec(file)) {
|
if (isE2eSpec(file)) {
|
||||||
addSpec(selection, file, selectedProjects);
|
selection.directSpecFiles.push(file);
|
||||||
selection.mappedFiles.push(file);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,13 +296,26 @@ function selectChangedTests(changedFiles) {
|
|||||||
|
|
||||||
selection.mappedFiles.push(file);
|
selection.mappedFiles.push(file);
|
||||||
for (const mapping of matches) {
|
for (const mapping of matches) {
|
||||||
const projects = mapping.projects.filter((project) => selectedProjects.includes(project));
|
const mappedProjects = mapping.projects.length > 0 ? mapping.projects : selectedProjects;
|
||||||
|
const projects = mappedProjects.filter((project) => selectedProjects.includes(project));
|
||||||
|
if (projects.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (const spec of mapping.specs) {
|
for (const spec of mapping.specs) {
|
||||||
addSpec(selection, spec, projects.length > 0 ? projects : selectedProjects);
|
addSpec(selection, spec, projects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selection.specProjects.size === 0 && !selection.fallback) {
|
||||||
|
for (const file of selection.directSpecFiles) {
|
||||||
|
addSpec(selection, file, selectedProjects);
|
||||||
|
selection.mappedFiles.push(file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selection.skippedDirectSpecFiles.push(...selection.directSpecFiles);
|
||||||
|
}
|
||||||
|
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +384,12 @@ async function runChangedSelection(selection) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selection.skippedDirectSpecFiles.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`[playwright-pr] Source mappings selected changed-area specs; direct E2E file edits are covered by mapped/core gates: ${selection.skippedDirectSpecFiles.join(", ")}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (const [index, group] of groups.entries()) {
|
for (const [index, group] of groups.entries()) {
|
||||||
for (const project of group.projects) {
|
for (const project of group.projects) {
|
||||||
const code = await runPlaywright({
|
const code = await runPlaywright({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Swal from "sweetalert2";
|
|||||||
import CustomerProductDiscountDisplay
|
import CustomerProductDiscountDisplay
|
||||||
from "@/components/displays/department/pos/displays/CustomerProductDiscountDisplay.vue";
|
from "@/components/displays/department/pos/displays/CustomerProductDiscountDisplay.vue";
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { isProductRestrictedForCustomer } from "@/features/customer/customerProductRules.js";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const expandIcon = ref(null);
|
const expandIcon = ref(null);
|
||||||
@@ -83,23 +84,6 @@ const hasRestrictAdditionalServices = () => {
|
|||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictAdditionalServices');
|
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictAdditionalServices');
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasRestrictSpotFree = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictSpotFree');
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasRestrictInteriorCleaning = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictInteriorCleaning');
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasRestrictTankCleaning = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictTankCleaning');
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasOnlyTankCleaning = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'onlyTankCleaning');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On before add product addon
|
* On before add product addon
|
||||||
* This is used to check if adding a product has any requirements.
|
* This is used to check if adding a product has any requirements.
|
||||||
@@ -162,37 +146,19 @@ const doesAddonHaveNoteRequirement = (addon) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isAddonRestricted = (addon) => {
|
const isAddonRestricted = (addon) => {
|
||||||
const addonName = getAddonName(addon).toLowerCase();
|
const addonProduct = getAddonProduct(addon);
|
||||||
// Check if the addon has "Spot Free" in the name
|
return isProductRestrictedForCustomer({
|
||||||
if (hasRestrictSpotFree() && addonName.includes('spot free')) {
|
...addonProduct,
|
||||||
return true;
|
name: getAddonName(addon) || addonProduct.name,
|
||||||
}
|
category: addonProduct.category ?? addon?.category,
|
||||||
// Check if the addon has "Indvendig vask" in the name
|
}, customerAttributesSafe.value);
|
||||||
if (hasRestrictInteriorCleaning() && addonName.includes('indvendig vask')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isProductRestricted = () => {
|
const isProductRestricted = () => {
|
||||||
const productName = String(props.name || '').toLowerCase();
|
return isProductRestrictedForCustomer({
|
||||||
// Check if the product has "Spot Free" in the name
|
...props.product,
|
||||||
if (hasRestrictSpotFree() && productName.includes('spot free')) {
|
name: props.name || props.product?.name,
|
||||||
return true;
|
}, customerAttributesSafe.value);
|
||||||
}
|
|
||||||
// Check if the product has "Indvendig vask" in the name
|
|
||||||
if (hasRestrictInteriorCleaning() && productName.includes('indvendig vask')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the product category is 5 (Tank Cleaning)
|
|
||||||
if (props.product?.category === 5) {
|
|
||||||
if (hasRestrictTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!hasOnlyTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const orderByOrderPriority = (addons) => {
|
const orderByOrderPriority = (addons) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useWindowSize } from "@vueuse/core";
|
|||||||
import { BMessage } from "buefy";
|
import { BMessage } from "buefy";
|
||||||
|
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
|
import { formatLocalDateOnly, parseLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
type DateRange = {
|
type DateRange = {
|
||||||
startDate: Date,
|
startDate: Date,
|
||||||
@@ -115,23 +116,11 @@ const availableYears = computed(() => (
|
|||||||
));
|
));
|
||||||
|
|
||||||
const formatDateInputValue = (date: Date) => {
|
const formatDateInputValue = (date: Date) => {
|
||||||
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
return formatLocalDateOnly(date);
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
||||||
const day = String(date.getDate()).padStart(2, "0");
|
|
||||||
return `${year}-${month}-${day}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseDateInputValue = (value: string) => {
|
const parseDateInputValue = (value: string) => {
|
||||||
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
return parseLocalDateOnly(value);
|
||||||
if (!match) {
|
|
||||||
return new Date(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSameDateInputValue = (left: Date, right: Date) => (
|
const isSameDateInputValue = (left: Date, right: Date) => (
|
||||||
|
|||||||
+72
-16
@@ -4,12 +4,15 @@ import { useI18n } from 'vue-i18n';
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
|
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
|
||||||
import { loadList } from "@/components/pagination/paginatedList.vue";
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
objects: {
|
objects: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
loadList: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
@@ -22,6 +25,52 @@ const su_object = SessionUser.objects.department_notification_sms;
|
|||||||
const hasUpdatePermission = () => {
|
const hasUpdatePermission = () => {
|
||||||
return (SessionUser.hasPermission('department_notification_sms_update') || SessionUser.canAccessSuperUser());
|
return (SessionUser.hasPermission('department_notification_sms_update') || SessionUser.canAccessSuperUser());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enabledSavingIds = ref(new Set());
|
||||||
|
|
||||||
|
const isEnabled = (value) => {
|
||||||
|
return value === true || value === 1 || value === "1" || value === "true";
|
||||||
|
};
|
||||||
|
|
||||||
|
const isEnabledSaving = (id) => {
|
||||||
|
return enabledSavingIds.value.has(Number(id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setEnabledSaving = (id, saving) => {
|
||||||
|
const nextIds = new Set(enabledSavingIds.value);
|
||||||
|
const normalizedId = Number(id);
|
||||||
|
if (saving) {
|
||||||
|
nextIds.add(normalizedId);
|
||||||
|
} else {
|
||||||
|
nextIds.delete(normalizedId);
|
||||||
|
}
|
||||||
|
enabledSavingIds.value = nextIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleEnabled = async (obj, event) => {
|
||||||
|
const target = event?.target;
|
||||||
|
const previousValue = isEnabled(obj.enabled);
|
||||||
|
|
||||||
|
if (!target || !hasUpdatePermission() || isEnabledSaving(obj.id)) {
|
||||||
|
if (target) {
|
||||||
|
target.checked = previousValue;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextValue = Boolean(target.checked);
|
||||||
|
setEnabledSaving(obj.id, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await su_object.set.enabled(obj.id, nextValue);
|
||||||
|
await props.loadList();
|
||||||
|
} catch (error) {
|
||||||
|
target.checked = previousValue;
|
||||||
|
console.error('Unable to update department notification SMS active state', error);
|
||||||
|
} finally {
|
||||||
|
setEnabledSaving(obj.id, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -42,7 +91,7 @@ const hasUpdatePermission = () => {
|
|||||||
<!-- Label -->
|
<!-- Label -->
|
||||||
<EditableTableColumn
|
<EditableTableColumn
|
||||||
:object="obj"
|
:object="obj"
|
||||||
:loadList="loadList"
|
:loadList="props.loadList"
|
||||||
:editFunction="su_object.showEditObjectFieldForm"
|
:editFunction="su_object.showEditObjectFieldForm"
|
||||||
column="label"
|
column="label"
|
||||||
:permission-check-function="hasUpdatePermission"
|
:permission-check-function="hasUpdatePermission"
|
||||||
@@ -50,7 +99,7 @@ const hasUpdatePermission = () => {
|
|||||||
<!-- Phone country code -->
|
<!-- Phone country code -->
|
||||||
<EditableTableColumn
|
<EditableTableColumn
|
||||||
:object="obj"
|
:object="obj"
|
||||||
:loadList="loadList"
|
:loadList="props.loadList"
|
||||||
:editFunction="su_object.showEditObjectFieldForm"
|
:editFunction="su_object.showEditObjectFieldForm"
|
||||||
column="phone_country_code"
|
column="phone_country_code"
|
||||||
:permission-check-function="hasUpdatePermission"
|
:permission-check-function="hasUpdatePermission"
|
||||||
@@ -58,22 +107,27 @@ const hasUpdatePermission = () => {
|
|||||||
<!-- Phone -->
|
<!-- Phone -->
|
||||||
<EditableTableColumn
|
<EditableTableColumn
|
||||||
:object="obj"
|
:object="obj"
|
||||||
:loadList="loadList"
|
:loadList="props.loadList"
|
||||||
:editFunction="su_object.showEditObjectFieldForm"
|
:editFunction="su_object.showEditObjectFieldForm"
|
||||||
column="phone"
|
column="phone"
|
||||||
:permission-check-function="hasUpdatePermission"
|
:permission-check-function="hasUpdatePermission"
|
||||||
/>
|
/>
|
||||||
<!-- Enabled -->
|
<!-- Enabled -->
|
||||||
<EditableTableColumn
|
<td>
|
||||||
:object="obj"
|
<input
|
||||||
:loadList="loadList"
|
:id="`department-notification-sms-enabled-${obj.id}`"
|
||||||
:editFunction="su_object.showEditObjectFieldForm"
|
class="switch is-rounded is-small"
|
||||||
column="enabled"
|
type="checkbox"
|
||||||
:parse-function="(value) => {
|
:checked="isEnabled(obj.enabled)"
|
||||||
return value ? t('common.yes') : t('common.no');
|
:disabled="!hasUpdatePermission() || isEnabledSaving(obj.id)"
|
||||||
}"
|
:data-testid="`department-notification-sms-enabled-toggle-${obj.id}`"
|
||||||
:permission-check-function="hasUpdatePermission"
|
:aria-label="`${su_object.columns.enabled.label}: ${obj.label}`"
|
||||||
/>
|
@change="toggleEnabled(obj, $event)"
|
||||||
|
/>
|
||||||
|
<label :for="`department-notification-sms-enabled-${obj.id}`">
|
||||||
|
{{ isEnabled(obj.enabled) ? t('common.yes') : t('common.no') }}
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<td>
|
<td>
|
||||||
<ActionSettingsWheelButton class="is-float-right">
|
<ActionSettingsWheelButton class="is-float-right">
|
||||||
@@ -81,9 +135,10 @@ const hasUpdatePermission = () => {
|
|||||||
<RequiresPermission :permission="'department_notification_sms_delete'">
|
<RequiresPermission :permission="'department_notification_sms_delete'">
|
||||||
<template #default>
|
<template #default>
|
||||||
<ActionSettingsWheelItem
|
<ActionSettingsWheelItem
|
||||||
@click="su_object.functions.showDeleteObjectForm(obj.id, loadList)"
|
@click="su_object.functions.showDeleteObjectForm(obj.id, props.loadList)"
|
||||||
:icon="SessionUser.objects.global.icons.delete"
|
:icon="SessionUser.objects.global.icons.delete"
|
||||||
:label="t('global.delete')"
|
:label="t('global.delete')"
|
||||||
|
:test-id="`department-notification-sms-delete-${obj.id}`"
|
||||||
template="danger"
|
template="danger"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -91,6 +146,7 @@ const hasUpdatePermission = () => {
|
|||||||
<ActionSettingsWheelItem
|
<ActionSettingsWheelItem
|
||||||
:icon="SessionUser.objects.global.icons.delete"
|
:icon="SessionUser.objects.global.icons.delete"
|
||||||
:label="t('global.delete')"
|
:label="t('global.delete')"
|
||||||
|
:test-id="`department-notification-sms-delete-${obj.id}`"
|
||||||
template="danger"
|
template="danger"
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
@@ -111,4 +167,4 @@ const hasUpdatePermission = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+20
-4
@@ -24,8 +24,24 @@ const onCustomerChange = () => {
|
|||||||
isCustomerSelected.value = false;
|
isCustomerSelected.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseValue = (value) => {
|
const parseFixedPriceValue = (value) => {
|
||||||
let tmp_value = parseFloat(value).toFixed(2);
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = Number.parseInt(String(value), 10);
|
||||||
|
return Number.isFinite(parsed) ? parsed : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatNumber = (value) => Number.isInteger(value) ? String(value) : value.toFixed(2);
|
||||||
|
|
||||||
|
const parseValue = (discount) => {
|
||||||
|
const fixedPrice = parseFixedPriceValue(discount.fixed_price);
|
||||||
|
if (fixedPrice !== null) {
|
||||||
|
return `${formatNumber(fixedPrice)} Kr.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmp_value = parseFloat(discount.percentage).toFixed(2);
|
||||||
// Add the percentage sign
|
// Add the percentage sign
|
||||||
return `${tmp_value}%`;
|
return `${tmp_value}%`;
|
||||||
}
|
}
|
||||||
@@ -78,7 +94,7 @@ watch(customer_id, onCustomerChange, { immediate: true });
|
|||||||
<!--<span class="icon is-small mr-1">
|
<!--<span class="icon is-small mr-1">
|
||||||
<i class="fas fa-percent" aria-hidden="true"></i>
|
<i class="fas fa-percent" aria-hidden="true"></i>
|
||||||
</span> -->
|
</span> -->
|
||||||
<span>{{ parseValue(discount.percentage) }}</span>
|
<span>{{ parseValue(discount) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,4 +102,4 @@ watch(customer_id, onCustomerChange, { immediate: true });
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ const discount_product = ref(null);
|
|||||||
const discount_category = ref(null);
|
const discount_category = ref(null);
|
||||||
// The global discount (if any)
|
// The global discount (if any)
|
||||||
const discount_global = ref(null);
|
const discount_global = ref(null);
|
||||||
|
// The fixed product price (if any)
|
||||||
|
const fixed_product_price = ref(null);
|
||||||
|
|
||||||
// Show the discounts dropdown
|
// Show the discounts dropdown
|
||||||
const showDiscountsDropdown = ref(false);
|
const showDiscountsDropdown = ref(false);
|
||||||
@@ -49,6 +51,19 @@ const hasGlobalDiscount = () => {
|
|||||||
return discount_global.value > 0;
|
return discount_global.value > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasFixedProductPrice = () => {
|
||||||
|
return fixed_product_price.value !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseFixedPriceValue = (value) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = Number.parseInt(String(value), 10);
|
||||||
|
return Number.isFinite(parsed) ? parsed : null;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the product discount
|
// Set the product discount
|
||||||
const setProductDiscount = (percentage) => {
|
const setProductDiscount = (percentage) => {
|
||||||
// Check if the percentage is a number
|
// Check if the percentage is a number
|
||||||
@@ -83,6 +98,16 @@ const setGlobalDiscount = (percentage) => {
|
|||||||
discount_global.value = percentage;
|
discount_global.value = percentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setFixedProductPrice = (fixedPrice) => {
|
||||||
|
const parsedFixedPrice = parseFixedPriceValue(fixedPrice);
|
||||||
|
if (parsedFixedPrice === null) {
|
||||||
|
fixed_product_price.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_product_price.value = parsedFixedPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Debugging function
|
// Debugging function
|
||||||
const getDiscountDebug = () => {
|
const getDiscountDebug = () => {
|
||||||
@@ -101,9 +126,15 @@ const getDiscountDebug = () => {
|
|||||||
|
|
||||||
// Parse the customer discounts
|
// Parse the customer discounts
|
||||||
const parseCustomerDiscounts = () => {
|
const parseCustomerDiscounts = () => {
|
||||||
const tmp_discount_product = props.customer_discounts.find((discount) => discount.product_or_category_id === props.product.id && !discount.is_category)
|
discount_product.value = null;
|
||||||
const tmp_discount_category = props.customer_discounts.find((discount) => discount.product_or_category_id === props.product.category && discount.is_category);
|
discount_category.value = null;
|
||||||
const tmp_discount_global = props.customer_discounts.find((discount) => discount.id === 999999 && discount.is_category);
|
discount_global.value = null;
|
||||||
|
fixed_product_price.value = null;
|
||||||
|
|
||||||
|
const customerDiscounts = Array.isArray(props.customer_discounts) ? props.customer_discounts : [];
|
||||||
|
const tmp_discount_product = customerDiscounts.find((discount) => discount.product_or_category_id == props.product.id && Number(discount.is_category) === 0)
|
||||||
|
const tmp_discount_category = customerDiscounts.find((discount) => discount.product_or_category_id == props.product.category && Number(discount.is_category) === 1);
|
||||||
|
const tmp_discount_global = customerDiscounts.find((discount) => discount.id === 999999 && Number(discount.is_category) === 1);
|
||||||
|
|
||||||
//console.log("Product discount: ", tmp_discount_product);
|
//console.log("Product discount: ", tmp_discount_product);
|
||||||
//console.log("Category discount: ", tmp_discount_category);
|
//console.log("Category discount: ", tmp_discount_category);
|
||||||
@@ -112,6 +143,7 @@ const parseCustomerDiscounts = () => {
|
|||||||
// Set the product discount
|
// Set the product discount
|
||||||
if (tmp_discount_product) {
|
if (tmp_discount_product) {
|
||||||
setProductDiscount(tmp_discount_product.percentage);
|
setProductDiscount(tmp_discount_product.percentage);
|
||||||
|
setFixedProductPrice(tmp_discount_product.fixed_price);
|
||||||
}
|
}
|
||||||
// Set the category discount
|
// Set the category discount
|
||||||
if (tmp_discount_category) {
|
if (tmp_discount_category) {
|
||||||
@@ -125,6 +157,10 @@ const parseCustomerDiscounts = () => {
|
|||||||
|
|
||||||
// Get the best discount for the customer
|
// Get the best discount for the customer
|
||||||
const getBestDiscount = () => {
|
const getBestDiscount = () => {
|
||||||
|
if (hasFixedProductPrice()) {
|
||||||
|
highestEligibleDiscount.value = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// Set the best discount to 0
|
// Set the best discount to 0
|
||||||
let tmp_best_discount = 0;
|
let tmp_best_discount = 0;
|
||||||
// Check if the product discount is higher than the current best discount
|
// Check if the product discount is higher than the current best discount
|
||||||
@@ -182,13 +218,32 @@ watch(() => props.customer_discounts, () => {
|
|||||||
<span
|
<span
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-controls="dropdown-menu"
|
aria-controls="dropdown-menu"
|
||||||
v-if="highestEligibleDiscount > 0"
|
v-if="hasFixedProductPrice()"
|
||||||
|
class="tag is-success is-light is-text text-can-not-select"
|
||||||
|
>{{ fixed_product_price }} Kr.</span>
|
||||||
|
<span
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-controls="dropdown-menu"
|
||||||
|
v-else-if="highestEligibleDiscount > 0"
|
||||||
class="tag is-warning is-light is-text text-can-not-select"
|
class="tag is-warning is-light is-text text-can-not-select"
|
||||||
> -{{ highestEligibleDiscount }}%</span>
|
> -{{ highestEligibleDiscount }}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||||
<div class="dropdown-content py-0">
|
<div class="dropdown-content py-0">
|
||||||
<div class="list has-overflow-ellipsis" style="width: 340px">
|
<div class="list has-overflow-ellipsis" style="width: 340px">
|
||||||
|
<template v-if="hasFixedProductPrice()">
|
||||||
|
<a class="list-item">
|
||||||
|
<div class="list-item-content">
|
||||||
|
<div class="list-item-title">Fast pris</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-item-controls list-item-controls-force-visible">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-success is-light">{{ fixed_product_price }} Kr.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<!-- Best discount -->
|
<!-- Best discount -->
|
||||||
<a class="list-item">
|
<a class="list-item">
|
||||||
<div class="list-item-content">
|
<div class="list-item-content">
|
||||||
@@ -239,6 +294,7 @@ watch(() => props.customer_discounts, () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -254,4 +310,4 @@ watch(() => props.customer_discounts, () => {
|
|||||||
.text-can-not-select {
|
.text-can-not-select {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, ref, watch } from "vue";
|
import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
||||||
import { useOrderMetadataAutosave } from "@/composables/useOrderMetadataAutosave.js";
|
import { useOrderMetadataAutosave } from "@/composables/useOrderMetadataAutosave.js";
|
||||||
import { isBlankPosMetadataValue } from "@/components/shop/POSDepartmentProcess.vue";
|
import {
|
||||||
|
isBlankPosMetadataValue,
|
||||||
|
registerPosStepSaveBarrier,
|
||||||
|
saveOrderMetadataField,
|
||||||
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
order_id: {
|
order_id: {
|
||||||
@@ -123,7 +126,7 @@ const referenceField = createCustomerWishField({
|
|||||||
key: "reference",
|
key: "reference",
|
||||||
label: computed(() => t("common.reference")),
|
label: computed(() => t("common.reference")),
|
||||||
source: () => props.reference,
|
source: () => props.reference,
|
||||||
saveValue: (value) => SessionUser.objects.orders.set.reference(props.order_id, value),
|
saveValue: (value) => saveOrderMetadataField("reference", value, props.order_id),
|
||||||
testIdBase: "pos-order-customer-wishes-reference",
|
testIdBase: "pos-order-customer-wishes-reference",
|
||||||
isRequired: () => props.referenceRequired,
|
isRequired: () => props.referenceRequired,
|
||||||
warningStateWhenEmpty: "danger",
|
warningStateWhenEmpty: "danger",
|
||||||
@@ -134,7 +137,7 @@ const poField = createCustomerWishField({
|
|||||||
key: "po",
|
key: "po",
|
||||||
label: computed(() => t("admin.pos.po_number")),
|
label: computed(() => t("admin.pos.po_number")),
|
||||||
source: () => props.po,
|
source: () => props.po,
|
||||||
saveValue: (value) => SessionUser.objects.orders.set.po(props.order_id, value),
|
saveValue: (value) => saveOrderMetadataField("po", value, props.order_id),
|
||||||
testIdBase: "pos-order-customer-wishes-po",
|
testIdBase: "pos-order-customer-wishes-po",
|
||||||
isRequired: () => props.poRequired,
|
isRequired: () => props.poRequired,
|
||||||
warningStateWhenEmpty: "warning",
|
warningStateWhenEmpty: "warning",
|
||||||
@@ -145,7 +148,7 @@ const safetySealField = createCustomerWishField({
|
|||||||
key: "safety_seal",
|
key: "safety_seal",
|
||||||
label: computed(() => t("admin.pos.safety_seal")),
|
label: computed(() => t("admin.pos.safety_seal")),
|
||||||
source: () => props.safetySeal,
|
source: () => props.safetySeal,
|
||||||
saveValue: (value) => SessionUser.objects.orders.set.safety_seal(props.order_id, value),
|
saveValue: (value) => saveOrderMetadataField("safety_seal", value, props.order_id),
|
||||||
testIdBase: "pos-order-customer-wishes-safety-seal",
|
testIdBase: "pos-order-customer-wishes-safety-seal",
|
||||||
isRequired: () => false,
|
isRequired: () => false,
|
||||||
warningStateWhenEmpty: null,
|
warningStateWhenEmpty: null,
|
||||||
@@ -161,6 +164,15 @@ const safetySealField = createCustomerWishField({
|
|||||||
const fields = computed(() => {
|
const fields = computed(() => {
|
||||||
return props.showSafetySeal ? [referenceField, poField, safetySealField] : [referenceField, poField];
|
return props.showSafetySeal ? [referenceField, poField, safetySealField] : [referenceField, poField];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const flushCustomerWishFields = async () => {
|
||||||
|
await Promise.all(fields.value.map((field) => field.autosave.flushAndWait()));
|
||||||
|
};
|
||||||
|
const unregisterStepSaveBarrier = registerPosStepSaveBarrier(flushCustomerWishFields);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
unregisterStepSaveBarrier();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, ref, watch } from 'vue';
|
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
||||||
import { useOrderMetadataAutosave } from "@/composables/useOrderMetadataAutosave.js";
|
import { useOrderMetadataAutosave } from "@/composables/useOrderMetadataAutosave.js";
|
||||||
|
import {
|
||||||
|
registerPosStepSaveBarrier,
|
||||||
|
saveOrderMetadataField,
|
||||||
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
order_id: {
|
order_id: {
|
||||||
@@ -30,8 +33,7 @@ const isEditing = ref(false);
|
|||||||
const autosave = useOrderMetadataAutosave({
|
const autosave = useOrderMetadataAutosave({
|
||||||
source: () => props.note,
|
source: () => props.note,
|
||||||
saveValue: async (value) => {
|
saveValue: async (value) => {
|
||||||
await SessionUser.objects.orders.set.notes(props.order_id, value);
|
return saveOrderMetadataField("notes", value, props.order_id);
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
onSaved: async (savedValue, requestedValue) => {
|
onSaved: async (savedValue, requestedValue) => {
|
||||||
if (typeof props.onSaved === "function") {
|
if (typeof props.onSaved === "function") {
|
||||||
@@ -68,6 +70,12 @@ const lostFocus = async () => {
|
|||||||
isEditing.value = false;
|
isEditing.value = false;
|
||||||
await autosave.flush();
|
await autosave.flush();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unregisterStepSaveBarrier = registerPosStepSaveBarrier(() => autosave.flushAndWait());
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
unregisterStepSaveBarrier();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, ref, watch } from "vue";
|
import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
||||||
import { useOrderMetadataAutosave } from "@/composables/useOrderMetadataAutosave.js";
|
import { useOrderMetadataAutosave } from "@/composables/useOrderMetadataAutosave.js";
|
||||||
|
import {
|
||||||
|
registerPosStepSaveBarrier,
|
||||||
|
saveOrderMetadataField,
|
||||||
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
index: {
|
index: {
|
||||||
@@ -36,8 +39,7 @@ autosave = useOrderMetadataAutosave({
|
|||||||
source: () => props.modelValue,
|
source: () => props.modelValue,
|
||||||
normalizeValue: normalizeRegistrationDraft,
|
normalizeValue: normalizeRegistrationDraft,
|
||||||
saveValue: async (value) => {
|
saveValue: async (value) => {
|
||||||
await SessionUser.objects.orders.set[fieldKey.value](props.order_id, value);
|
return saveOrderMetadataField(fieldKey.value, value, props.order_id);
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
onSaved: async () => {
|
onSaved: async () => {
|
||||||
await props.loadOrder();
|
await props.loadOrder();
|
||||||
@@ -74,6 +76,12 @@ const closeEditor = async () => {
|
|||||||
isEditing.value = false;
|
isEditing.value = false;
|
||||||
await autosave.flush();
|
await autosave.flush();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unregisterStepSaveBarrier = registerPosStepSaveBarrier(() => autosave.flushAndWait());
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
unregisterStepSaveBarrier();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -817,6 +817,10 @@ const isDraftAssignmentModalOpen = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const canEditTransaction = (order) => {
|
const canEditTransaction = (order) => {
|
||||||
|
if (props.isCustomerView) {
|
||||||
|
return SessionUser.canAccessCustomerFeature("orders", "edit");
|
||||||
|
}
|
||||||
|
|
||||||
return SessionUser.canAccessAdmin() || SessionUser.canAccessDepartment(order.department_id);
|
return SessionUser.canAccessAdmin() || SessionUser.canAccessDepartment(order.department_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1259,7 +1263,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.reference"
|
:hover-text="order.reference"
|
||||||
:parse-function="(value) => truncateOrderField(value, 10)"
|
:parse-function="(value) => truncateOrderField(value, 10)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => true"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
<EditableTableColumn
|
<EditableTableColumn
|
||||||
:object="order"
|
:object="order"
|
||||||
@@ -1278,7 +1282,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.po"
|
:hover-text="order.po"
|
||||||
:parse-function="(value) => truncateOrderField(value, 10)"
|
:parse-function="(value) => truncateOrderField(value, 10)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => canSeePoField() || SessionUser.canAccessAdmin()"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
<td>{{ order.created_at }}</td>
|
<td>{{ order.created_at }}</td>
|
||||||
<td>{{ SessionUser.functions.currency.toLocal(order.total_net_amount) }}</td>
|
<td>{{ SessionUser.functions.currency.toLocal(order.total_net_amount) }}</td>
|
||||||
@@ -1774,7 +1778,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.reference"
|
:hover-text="order.reference"
|
||||||
:parse-function="(value) => truncateOrderField(value, 20)"
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => true"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -1818,7 +1822,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.po"
|
:hover-text="order.po"
|
||||||
:parse-function="(value) => truncateOrderField(value, 20)"
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => canSeePoField() || SessionUser.canAccessAdmin()"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -1863,7 +1867,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.reg_1"
|
:hover-text="order.reg_1"
|
||||||
:parse-function="(value) => truncateOrderField(value, 20)"
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => true"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -1895,7 +1899,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.reg_2"
|
:hover-text="order.reg_2"
|
||||||
:parse-function="(value) => truncateOrderField(value, 20)"
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => true"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -1927,7 +1931,7 @@ const formatCashierName = (order) => {
|
|||||||
:hover-text="order.reg_3"
|
:hover-text="order.reg_3"
|
||||||
:parse-function="(value) => truncateOrderField(value, 20)"
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
||||||
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
||||||
:permission-check-function="() => true"
|
:permission-check-function="() => canEditTransaction(order)"
|
||||||
/>
|
/>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import PosLastScannedLicensePlatesV2 from "@/components/displays/department/pos/
|
|||||||
import PosDesktopOrderBookingSelectorModal from "@/components/displays/department/pos/steps/elements/PosDesktopOrderBookingSelectorModal.vue";
|
import PosDesktopOrderBookingSelectorModal from "@/components/displays/department/pos/steps/elements/PosDesktopOrderBookingSelectorModal.vue";
|
||||||
import PosDesktopCustomerConflictModal from "@/components/displays/department/pos/steps/elements/PosDesktopCustomerConflictModal.vue";
|
import PosDesktopCustomerConflictModal from "@/components/displays/department/pos/steps/elements/PosDesktopCustomerConflictModal.vue";
|
||||||
import PosDesktopDuplicateWarning from "@/components/displays/department/pos/steps/elements/PosDesktopDuplicateWarning.vue";
|
import PosDesktopDuplicateWarning from "@/components/displays/department/pos/steps/elements/PosDesktopDuplicateWarning.vue";
|
||||||
|
import { todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import { parsePosRouteSearch } from "@/views/dashboards/departmentDashboard/modules/Pos/posRouteState.js";
|
import { parsePosRouteSearch } from "@/views/dashboards/departmentDashboard/modules/Pos/posRouteState.js";
|
||||||
import {
|
import {
|
||||||
@@ -377,8 +378,8 @@ const fetchDuplicateOrdersForContext = async (context) => {
|
|||||||
try {
|
try {
|
||||||
const response = await SessionUser.request(SessionUser.objects.orders.meta.endpoint, "GET", {
|
const response = await SessionUser.request(SessionUser.objects.orders.meta.endpoint, "GET", {
|
||||||
filters: `reg_1:${normalizedContext.reg1},department_id:${department_id.value},created_at-date_from:${
|
filters: `reg_1:${normalizedContext.reg1},department_id:${department_id.value},created_at-date_from:${
|
||||||
new Date().toISOString().split("T")[0]
|
todayLocalDateOnly()
|
||||||
},created_at-date_to:${new Date().toISOString().split("T")[0]}`,
|
},created_at-date_to:${todayLocalDateOnly()}`,
|
||||||
limit: 5,
|
limit: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
setBackgroundColor,
|
setBackgroundColor,
|
||||||
backgroundColors,
|
backgroundColors,
|
||||||
} from "@/components/viewport/page/headers/ViewportHeaderSettings.vue";
|
} from "@/components/viewport/page/headers/ViewportHeaderSettings.vue";
|
||||||
import { onMounted, ref, watch, computed } from "vue";
|
import { onBeforeUnmount, onMounted, ref, watch, computed } from "vue";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import PosDepartmentStepMobile2Product from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2Product.vue";
|
import PosDepartmentStepMobile2Product from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2Product.vue";
|
||||||
import PosDepartmentStepMobile2LastOrder from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2LastOrder.vue";
|
import PosDepartmentStepMobile2LastOrder from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2LastOrder.vue";
|
||||||
@@ -43,6 +43,8 @@ import {
|
|||||||
customer_name,
|
customer_name,
|
||||||
isAddonRestricted,
|
isAddonRestricted,
|
||||||
canBuyAdditionalServices,
|
canBuyAdditionalServices,
|
||||||
|
registerPosStepSaveBarrier,
|
||||||
|
saveOrderMetadataField,
|
||||||
} from "@/components/shop/POSDepartmentProcess.vue";
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import { createOrderItem, getOrderItems, removeOrderItem } from "@/components/shop/OrdersItems.vue";
|
import { createOrderItem, getOrderItems, removeOrderItem } from "@/components/shop/OrdersItems.vue";
|
||||||
import { PosProduct } from "@/components/displays/department/pos/steps/mobile/objects/PosProduct.vue";
|
import { PosProduct } from "@/components/displays/department/pos/steps/mobile/objects/PosProduct.vue";
|
||||||
@@ -196,8 +198,7 @@ const applyPendingBookingFromSelection = async () => {
|
|||||||
// Set PO number on the order if provided by the booking
|
// Set PO number on the order if provided by the booking
|
||||||
if (booking.po && typeof booking.po === "string" && booking.po.trim() !== "") {
|
if (booking.po && typeof booking.po === "string" && booking.po.trim() !== "") {
|
||||||
try {
|
try {
|
||||||
await SessionUser.objects.orders.set.po(order_id.value, booking.po);
|
order_po.value = await saveOrderMetadataField("po", booking.po, order_id.value);
|
||||||
order_po.value = booking.po.trim();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to set PO on order from booking", e);
|
console.error("Failed to set PO on order from booking", e);
|
||||||
}
|
}
|
||||||
@@ -217,9 +218,9 @@ const applyPendingBookingFromSelection = async () => {
|
|||||||
const bookingReference = String(fullBookingData.reference ?? fullBookingData.reference_number ?? "").trim();
|
const bookingReference = String(fullBookingData.reference ?? fullBookingData.reference_number ?? "").trim();
|
||||||
if (bookingReference !== "") {
|
if (bookingReference !== "") {
|
||||||
try {
|
try {
|
||||||
await SessionUser.objects.orders.set.reference(order_id.value, bookingReference);
|
const savedReference = await saveOrderMetadataField("reference", bookingReference, order_id.value);
|
||||||
persistedReference.value = bookingReference;
|
persistedReference.value = savedReference;
|
||||||
metadata.setReference(bookingReference);
|
metadata.setReference(savedReference);
|
||||||
hydrateReferenceFromSources();
|
hydrateReferenceFromSources();
|
||||||
} catch (setRefError) {
|
} catch (setRefError) {
|
||||||
console.error("Failed to set reference on order from booking:", setRefError);
|
console.error("Failed to set reference on order from booking:", setRefError);
|
||||||
@@ -228,10 +229,10 @@ const applyPendingBookingFromSelection = async () => {
|
|||||||
const bookingNotes = String(fullBookingData.notes ?? fullBookingData.note ?? "").trim();
|
const bookingNotes = String(fullBookingData.notes ?? fullBookingData.note ?? "").trim();
|
||||||
if (bookingNotes !== "") {
|
if (bookingNotes !== "") {
|
||||||
try {
|
try {
|
||||||
await SessionUser.objects.orders.set.notes(order_id.value, bookingNotes);
|
const savedNotes = await saveOrderMetadataField("notes", bookingNotes, order_id.value);
|
||||||
order_notes.value = bookingNotes;
|
order_notes.value = savedNotes;
|
||||||
notes.value = bookingNotes;
|
notes.value = savedNotes;
|
||||||
metadata.setNotes(bookingNotes);
|
metadata.setNotes(savedNotes);
|
||||||
} catch (setNotesError) {
|
} catch (setNotesError) {
|
||||||
console.error("Failed to set notes on order from booking:", setNotesError);
|
console.error("Failed to set notes on order from booking:", setNotesError);
|
||||||
}
|
}
|
||||||
@@ -243,13 +244,11 @@ const applyPendingBookingFromSelection = async () => {
|
|||||||
const currentReg2 = normalizeRegistrationValue(reg_2.value);
|
const currentReg2 = normalizeRegistrationValue(reg_2.value);
|
||||||
|
|
||||||
if (bookingReg1 && bookingReg1 !== currentReg1) {
|
if (bookingReg1 && bookingReg1 !== currentReg1) {
|
||||||
reg_1.value = bookingReg1;
|
reg_1.value = await saveOrderMetadataField("reg_1", bookingReg1, order_id.value);
|
||||||
await SessionUser.objects.orders.set.reg_1(order_id.value, bookingReg1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bookingReg2 !== currentReg2) {
|
if (bookingReg2 !== currentReg2) {
|
||||||
reg_2.value = bookingReg2;
|
reg_2.value = await saveOrderMetadataField("reg_2", bookingReg2, order_id.value);
|
||||||
await SessionUser.objects.orders.set.reg_2(order_id.value, bookingReg2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookingMatches = Array.isArray(vehicles.vehicle_1.value?.booking_matches)
|
const bookingMatches = Array.isArray(vehicles.vehicle_1.value?.booking_matches)
|
||||||
@@ -538,8 +537,7 @@ const notesAutosave = useOrderMetadataAutosave({
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SessionUser.objects.orders.set.notes(normalizedOrderId, value);
|
return saveOrderMetadataField("notes", value, normalizedOrderId);
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
onSaved: async (value) => {
|
onSaved: async (value) => {
|
||||||
order_notes.value = value;
|
order_notes.value = value;
|
||||||
@@ -555,8 +553,7 @@ const referenceAutosave = useOrderMetadataAutosave({
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SessionUser.objects.orders.set.reference(normalizedOrderId, value);
|
return saveOrderMetadataField("reference", value, normalizedOrderId);
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
onSaved: async (value) => {
|
onSaved: async (value) => {
|
||||||
persistedReference.value = value;
|
persistedReference.value = value;
|
||||||
@@ -572,8 +569,7 @@ const safetySealAutosave = useOrderMetadataAutosave({
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SessionUser.objects.orders.set.safety_seal(normalizedOrderId, value);
|
return saveOrderMetadataField("safety_seal", value, normalizedOrderId);
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
onSaved: async (value) => {
|
onSaved: async (value) => {
|
||||||
order_safety_seal.value = value;
|
order_safety_seal.value = value;
|
||||||
@@ -585,6 +581,18 @@ const stepTwoNotesInput = notesAutosave.draft;
|
|||||||
const stepTwoReferenceInput = referenceAutosave.draft;
|
const stepTwoReferenceInput = referenceAutosave.draft;
|
||||||
const stepTwoSafetySealInput = safetySealAutosave.draft;
|
const stepTwoSafetySealInput = safetySealAutosave.draft;
|
||||||
const showSafetySealField = computed(() => transactionItems.containsWashCertificate());
|
const showSafetySealField = computed(() => transactionItems.containsWashCertificate());
|
||||||
|
const flushStepTwoMetadataFields = async () => {
|
||||||
|
await Promise.all([
|
||||||
|
notesAutosave.flushAndWait(),
|
||||||
|
referenceAutosave.flushAndWait(),
|
||||||
|
safetySealAutosave.flushAndWait(),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
const unregisterStepSaveBarrier = registerPosStepSaveBarrier(flushStepTwoMetadataFields);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
unregisterStepSaveBarrier();
|
||||||
|
});
|
||||||
|
|
||||||
const resolveReferenceFromSources = () => {
|
const resolveReferenceFromSources = () => {
|
||||||
const orderReference = normalizeReferenceValue(persistedReference.value);
|
const orderReference = normalizeReferenceValue(persistedReference.value);
|
||||||
|
|||||||
+44
-7
@@ -22,7 +22,7 @@ import PosDepartmentStep2MobileVehicleSelection
|
|||||||
import GenericButton from "@/components/viewport/page/templates/generic/graphics/GenericButton.vue";
|
import GenericButton from "@/components/viewport/page/templates/generic/graphics/GenericButton.vue";
|
||||||
import PosDepartmentStepMobile2FloatingCart
|
import PosDepartmentStepMobile2FloatingCart
|
||||||
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2FloatingCart.vue";
|
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2FloatingCart.vue";
|
||||||
import { isAddonRestricted, canBuyAdditionalServices } from "@/components/shop/POSDepartmentProcess.vue";
|
import { isAddonRestricted, canBuyAdditionalServices, isProductRestricted } from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: {
|
label: {
|
||||||
@@ -46,6 +46,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const canSelectAdditionalItems = computed(() => canBuyAdditionalServices());
|
||||||
const checked = ref(props.defaultChecked);
|
const checked = ref(props.defaultChecked);
|
||||||
// Function to generate a summary from the last order
|
// Function to generate a summary from the last order
|
||||||
function generateSummary(order: PosOrder): string {
|
function generateSummary(order: PosOrder): string {
|
||||||
@@ -72,6 +73,12 @@ const displaySubtitle = computed(() => {
|
|||||||
});
|
});
|
||||||
// Emit event on toggle
|
// Emit event on toggle
|
||||||
function onToggle(isOpen: boolean) {
|
function onToggle(isOpen: boolean) {
|
||||||
|
if (isOpen && !canSelectAdditionalItems.value) {
|
||||||
|
checked.value = false;
|
||||||
|
pos.views.additionalItemSelection.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
checked.value = isOpen;
|
checked.value = isOpen;
|
||||||
// Open the additional item selection view if toggled open
|
// Open the additional item selection view if toggled open
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@@ -118,17 +125,45 @@ const getAvailableAdditionalItems = () => {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
const availableAdditionalItems = ref<Addon[]>(getAvailableAdditionalItems());
|
const availableAdditionalItems = ref<Addon[]>(getAvailableAdditionalItems());
|
||||||
|
const isAdditionalItemRestricted = (product: PosProduct) => {
|
||||||
|
if (!canSelectAdditionalItems.value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isProductRestricted(product)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isAddonRestricted(convertProductToAddon(product));
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(canSelectAdditionalItems, (canSelect) => {
|
||||||
|
if (!canSelect) {
|
||||||
|
checked.value = false;
|
||||||
|
pos.views.additionalItemSelection.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Computed property to filter out restricted additional items based on customer attributes
|
// Computed property to filter out restricted additional items based on customer attributes
|
||||||
const filteredAdditionalItems = computed(() => {
|
const filteredAdditionalItems = computed(() => {
|
||||||
// If additional services are restricted, return empty array
|
// If additional services are restricted, return empty array
|
||||||
if (!canBuyAdditionalServices()) {
|
if (!canSelectAdditionalItems.value) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
// Filter out individually restricted addons
|
// Filter out individually restricted addons
|
||||||
return availableAdditionalItems.value.filter((addon: Addon) => !isAddonRestricted(addon));
|
return availableAdditionalItems.value.filter((addon: Addon) => {
|
||||||
|
if (isAddonRestricted(addon)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (addon.product && isProductRestricted(addon.product)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
const onClickAddOtherProduct = () => {
|
const onClickAddOtherProduct = () => {
|
||||||
|
if (!canSelectAdditionalItems.value) {
|
||||||
|
pos.views.additionalItemSelection.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
pos.views.additionalItemSelection.value = !pos.views.additionalItemSelection.value;
|
pos.views.additionalItemSelection.value = !pos.views.additionalItemSelection.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +194,9 @@ watch(() => pos.transactionItems.additionalItems.value, (newVal) => {
|
|||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
const onClickAddProduct = async (product: PosProduct) => {
|
const onClickAddProduct = async (product: PosProduct) => {
|
||||||
// If the product requires note, open note input.
|
if (isAdditionalItemRestricted(product)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
pos.transactionItems.addAdditionalItem(product);
|
pos.transactionItems.addAdditionalItem(product);
|
||||||
// If the view is fullscreen, close it after adding
|
// If the view is fullscreen, close it after adding
|
||||||
//if (pos.views.additionalItemSelection.value) {
|
//if (pos.views.additionalItemSelection.value) {
|
||||||
@@ -171,7 +208,7 @@ const onClickAddProduct = async (product: PosProduct) => {
|
|||||||
<template>
|
<template>
|
||||||
<div data-testid="pos-mobile-additional-items">
|
<div data-testid="pos-mobile-additional-items">
|
||||||
<!-- Minimal view, when not set as fullscreen view -->
|
<!-- Minimal view, when not set as fullscreen view -->
|
||||||
<WhiteBoxCard :toggleable="pos.transactionItems.additionalItems.value && pos.transactionItems.additionalItems.value.length == 0"
|
<WhiteBoxCard :toggleable="canSelectAdditionalItems && pos.transactionItems.additionalItems.value && pos.transactionItems.additionalItems.value.length == 0"
|
||||||
:defaultOpen="pos.transactionItems.additionalItems.value && pos.transactionItems.additionalItems.value.length > 0"
|
:defaultOpen="pos.transactionItems.additionalItems.value && pos.transactionItems.additionalItems.value.length > 0"
|
||||||
@toggle="onToggle"
|
@toggle="onToggle"
|
||||||
:forceState="(pos.transactionItems.additionalItems.value && pos.transactionItems.additionalItems.value.length > 0) ? true : (pos.views.additionalItemSelection.value)"
|
:forceState="(pos.transactionItems.additionalItems.value && pos.transactionItems.additionalItems.value.length > 0) ? true : (pos.views.additionalItemSelection.value)"
|
||||||
@@ -201,7 +238,7 @@ const onClickAddProduct = async (product: PosProduct) => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<template #footer>
|
<template #footer v-if="canSelectAdditionalItems">
|
||||||
<!-- Select other product button -->
|
<!-- Select other product button -->
|
||||||
<a class="card-footer-item" data-testid="pos-mobile-additional-items-open" @click="onClickAddOtherProduct">
|
<a class="card-footer-item" data-testid="pos-mobile-additional-items-open" @click="onClickAddOtherProduct">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
@@ -212,7 +249,7 @@ const onClickAddProduct = async (product: PosProduct) => {
|
|||||||
</template>
|
</template>
|
||||||
</WhiteBoxCard>
|
</WhiteBoxCard>
|
||||||
<!-- Fullscreen view, when selecting other products -->
|
<!-- Fullscreen view, when selecting other products -->
|
||||||
<template v-else>
|
<template v-else-if="canSelectAdditionalItems">
|
||||||
<div data-testid="pos-mobile-additional-items-selection">
|
<div data-testid="pos-mobile-additional-items-selection">
|
||||||
<!-- Categories of products -->
|
<!-- Categories of products -->
|
||||||
<PosDepartmentStep2MobileVehicleSelection :onAddProduct="onClickAddProduct" :onSearchClick="() => console.warn('AdditionalItem Search Clicked')"/><!-- :asAddons="true" :addons="availableAdditionalItems" @update:addons="availableAdditionalItems = $event"/>-->
|
<PosDepartmentStep2MobileVehicleSelection :onAddProduct="onClickAddProduct" :onSearchClick="() => console.warn('AdditionalItem Search Clicked')"/><!-- :asAddons="true" :addons="availableAdditionalItems" @update:addons="availableAdditionalItems = $event"/>-->
|
||||||
|
|||||||
+35
-19
@@ -20,6 +20,7 @@ import {
|
|||||||
hasAttribute,
|
hasAttribute,
|
||||||
uploadAttachment,
|
uploadAttachment,
|
||||||
restoreStoredPosOrderId,
|
restoreStoredPosOrderId,
|
||||||
|
saveOrderMetadataField,
|
||||||
} from "@/components/shop/POSDepartmentProcess.vue";
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import * as POSDepartmentProcess from "@/components/shop/POSDepartmentProcess.vue";
|
import * as POSDepartmentProcess from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import { errors } from "@/components/request/HandleGlobalError.vue";
|
import { errors } from "@/components/request/HandleGlobalError.vue";
|
||||||
@@ -96,17 +97,30 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateReference = () => {
|
const getActiveOrderId = () => {
|
||||||
if (!!order_id.value && order_id.value > 0) SessionUser.objects.orders.set.reference(order_id.value, reference.value);
|
const parsedOrderId = Number.parseInt(String(order_id.value ?? ""), 10);
|
||||||
|
return Number.isInteger(parsedOrderId) && parsedOrderId > 0 ? parsedOrderId : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateNotes = () => {
|
const updateReference = async () => {
|
||||||
if (!!order_id.value && order_id.value > 0) SessionUser.objects.orders.set.notes(order_id.value, order_notes.value);
|
const activeOrderId = getActiveOrderId();
|
||||||
|
if (activeOrderId) {
|
||||||
|
await saveOrderMetadataField("reference", reference.value, activeOrderId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSafetySeal = () => {
|
const updateNotes = async () => {
|
||||||
if (!!order_id.value && order_id.value > 0)
|
const activeOrderId = getActiveOrderId();
|
||||||
SessionUser.objects.orders.set.safety_seal(order_id.value, order_safety_seal.value);
|
if (activeOrderId) {
|
||||||
|
await saveOrderMetadataField("notes", order_notes.value, activeOrderId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSafetySeal = async () => {
|
||||||
|
const activeOrderId = getActiveOrderId();
|
||||||
|
if (activeOrderId) {
|
||||||
|
await saveOrderMetadataField("safety_seal", order_safety_seal.value, activeOrderId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helpers: extracted for clarity and reuse
|
// Helpers: extracted for clarity and reuse
|
||||||
@@ -196,9 +210,9 @@ const step1 = async () => {
|
|||||||
// Reset the vehicle selection
|
// Reset the vehicle selection
|
||||||
views.vehicleSelection.value = false;
|
views.vehicleSelection.value = false;
|
||||||
// Merge references and notes
|
// Merge references and notes
|
||||||
mergeReferences();
|
await mergeReferences();
|
||||||
mergeNotes();
|
await mergeNotes();
|
||||||
mergeSafetySeal();
|
await mergeSafetySeal();
|
||||||
// We only need to check if the customer requires reference, if the field is empty.
|
// We only need to check if the customer requires reference, if the field is empty.
|
||||||
if (!isNonEmptyString(reference.value)) {
|
if (!isNonEmptyString(reference.value)) {
|
||||||
const isValid = await validateReferenceRequirements();
|
const isValid = await validateReferenceRequirements();
|
||||||
@@ -214,37 +228,39 @@ const mergeReferences = () => {
|
|||||||
if (metadata.getReference() && metadata.getReference().length >= 1) {
|
if (metadata.getReference() && metadata.getReference().length >= 1) {
|
||||||
// If there is no reference, set it to the registration number of vehicle 1
|
// If there is no reference, set it to the registration number of vehicle 1
|
||||||
reference.value = metadata.getReference();
|
reference.value = metadata.getReference();
|
||||||
updateReference();
|
return updateReference();
|
||||||
} else if (reference.value && reference.value.length >= 1) {
|
} else if (reference.value && reference.value.length >= 1) {
|
||||||
// If there is a reference in the field, set it to the metadata
|
// If there is a reference in the field, set it to the metadata
|
||||||
metadata.setReference(reference.value);
|
metadata.setReference(reference.value);
|
||||||
updateReference();
|
return updateReference();
|
||||||
} else if (vehicles.vehicle_1.value?.reference && vehicles.vehicle_1.value?.reference.length >= 1) {
|
} else if (vehicles.vehicle_1.value?.reference && vehicles.vehicle_1.value?.reference.length >= 1) {
|
||||||
// If there is no reference, set it to the registration number of vehicle 1
|
// If there is no reference, set it to the registration number of vehicle 1
|
||||||
reference.value = vehicles.vehicle_1.value?.reference;
|
reference.value = vehicles.vehicle_1.value?.reference;
|
||||||
metadata.setReference(reference.value);
|
metadata.setReference(reference.value);
|
||||||
updateReference();
|
return updateReference();
|
||||||
}
|
}
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
const mergeNotes = () => {
|
const mergeNotes = () => {
|
||||||
if (metadata.getNotes() && metadata.getNotes().length >= 1) {
|
if (metadata.getNotes() && metadata.getNotes().length >= 1) {
|
||||||
order_notes.value = metadata.getNotes();
|
order_notes.value = metadata.getNotes();
|
||||||
updateNotes();
|
return updateNotes();
|
||||||
} else if (order_notes.value && order_notes.value.length >= 1) {
|
} else if (order_notes.value && order_notes.value.length >= 1) {
|
||||||
metadata.setNotes(order_notes.value);
|
metadata.setNotes(order_notes.value);
|
||||||
updateNotes();
|
return updateNotes();
|
||||||
}
|
}
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
const mergeSafetySeal = () => {
|
const mergeSafetySeal = () => {
|
||||||
const resolvedSafetySeal = getResolvedMobileSafetySeal();
|
const resolvedSafetySeal = getResolvedMobileSafetySeal();
|
||||||
order_safety_seal.value = resolvedSafetySeal;
|
order_safety_seal.value = resolvedSafetySeal;
|
||||||
syncMobileSafetySealState(resolvedSafetySeal);
|
syncMobileSafetySealState(resolvedSafetySeal);
|
||||||
updateSafetySeal();
|
return updateSafetySeal();
|
||||||
};
|
};
|
||||||
const finalizeStep1 = async () => {
|
const finalizeStep1 = async () => {
|
||||||
mergeReferences();
|
await mergeReferences();
|
||||||
mergeNotes();
|
await mergeNotes();
|
||||||
mergeSafetySeal();
|
await mergeSafetySeal();
|
||||||
// Upload the attachments if there are any (And the order id has been created)
|
// Upload the attachments if there are any (And the order id has been created)
|
||||||
if (attachments.getBase64().length > 0 && order_id.value > 0) {
|
if (attachments.getBase64().length > 0 && order_id.value > 0) {
|
||||||
const uploadResults = await Promise.all(
|
const uploadResults = await Promise.all(
|
||||||
|
|||||||
+19
@@ -62,6 +62,7 @@ watch(
|
|||||||
form.invoiceEmail,
|
form.invoiceEmail,
|
||||||
form.contactEmail,
|
form.contactEmail,
|
||||||
form.contactPhone,
|
form.contactPhone,
|
||||||
|
form.ean,
|
||||||
canCreateCustomer.value,
|
canCreateCustomer.value,
|
||||||
searchResult.value,
|
searchResult.value,
|
||||||
errorMessage.value,
|
errorMessage.value,
|
||||||
@@ -124,6 +125,24 @@ watch(
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label"
|
||||||
|
><small>{{ $t("admin.pos.ean") }}</small></label
|
||||||
|
>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
v-model="form.ean"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
maxlength="13"
|
||||||
|
:placeholder="$t('admin.pos.ean')"
|
||||||
|
:disabled="searchResult === null || submitting"
|
||||||
|
data-testid="pos-mobile-add-customer-ean"
|
||||||
|
@input="handleAutofillFieldInput('ean')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label"
|
<label class="label"
|
||||||
><small>{{ $t("admin.pos.invoice_email") }}</small></label
|
><small>{{ $t("admin.pos.invoice_email") }}</small></label
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export const POS_ADD_CUSTOMER_AUTOFILL_FIELDS = [
|
|||||||
"invoiceEmail",
|
"invoiceEmail",
|
||||||
"contactEmail",
|
"contactEmail",
|
||||||
"contactPhone",
|
"contactPhone",
|
||||||
|
"ean",
|
||||||
];
|
];
|
||||||
|
|
||||||
export function normalizePosAddCustomerValue(value) {
|
export function normalizePosAddCustomerValue(value) {
|
||||||
@@ -20,6 +21,7 @@ export function createPosAddCustomerFields(overrides = {}) {
|
|||||||
invoiceEmail: "",
|
invoiceEmail: "",
|
||||||
contactEmail: "",
|
contactEmail: "",
|
||||||
contactPhone: "",
|
contactPhone: "",
|
||||||
|
ean: "",
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -30,6 +32,7 @@ export function createPosAddCustomerManualOverrides(overrides = {}) {
|
|||||||
invoiceEmail: false,
|
invoiceEmail: false,
|
||||||
contactEmail: false,
|
contactEmail: false,
|
||||||
contactPhone: false,
|
contactPhone: false,
|
||||||
|
ean: false,
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -93,6 +96,7 @@ export function buildPosAddCustomerAutofillValues(searchResult) {
|
|||||||
invoiceEmail: normalizePosAddCustomerValue(searchResult?.email),
|
invoiceEmail: normalizePosAddCustomerValue(searchResult?.email),
|
||||||
contactEmail: normalizePosAddCustomerValue(searchResult?.email),
|
contactEmail: normalizePosAddCustomerValue(searchResult?.email),
|
||||||
contactPhone: normalizePosAddCustomerValue(searchResult?.phone),
|
contactPhone: normalizePosAddCustomerValue(searchResult?.phone),
|
||||||
|
ean: normalizePosAddCustomerValue(searchResult?.ean),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -5,6 +5,7 @@ import {
|
|||||||
order_safety_seal,
|
order_safety_seal,
|
||||||
reference,
|
reference,
|
||||||
clearStoredPosOrderId,
|
clearStoredPosOrderId,
|
||||||
|
saveOrderMetadataField,
|
||||||
} from "@/components/shop/POSDepartmentProcess.vue";
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import { metadata } from "./PosDepartmentStepMobileFlow.vue";
|
import { metadata } from "./PosDepartmentStepMobileFlow.vue";
|
||||||
|
|
||||||
@@ -52,9 +53,9 @@ export const persistCurrentMobileOrderMetadata = async () => {
|
|||||||
order_notes.value = normalizedNotes;
|
order_notes.value = normalizedNotes;
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
SessionUser.objects.orders.set.reference(normalizedOrderId, normalizedReference),
|
saveOrderMetadataField("reference", normalizedReference, normalizedOrderId),
|
||||||
SessionUser.objects.orders.set.notes(normalizedOrderId, normalizedNotes),
|
saveOrderMetadataField("notes", normalizedNotes, normalizedOrderId),
|
||||||
SessionUser.objects.orders.set.safety_seal(normalizedOrderId, normalizedSafetySeal),
|
saveOrderMetadataField("safety_seal", normalizedSafetySeal, normalizedOrderId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return normalizedOrderId;
|
return normalizedOrderId;
|
||||||
|
|||||||
+16
-5
@@ -1,6 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onBeforeUnmount, onMounted, watch } from "vue";
|
import { ref, onBeforeUnmount, onMounted, watch } from "vue";
|
||||||
import { order_id, reg_1, reg_2, reg_3, getOrderDetails } from "@/components/shop/POSDepartmentProcess.vue";
|
import {
|
||||||
|
order_id,
|
||||||
|
reg_1,
|
||||||
|
reg_2,
|
||||||
|
reg_3,
|
||||||
|
registerPosStepSaveBarrier,
|
||||||
|
saveOrderMetadataField,
|
||||||
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import PosDepartmentStepMobile1RegistrationNumberInputField from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile1RegistrationNumberInputField.vue";
|
import PosDepartmentStepMobile1RegistrationNumberInputField from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile1RegistrationNumberInputField.vue";
|
||||||
import BarcodeScanner from "@/components/viewport/elements/icons/BarcodeScanner.vue";
|
import BarcodeScanner from "@/components/viewport/elements/icons/BarcodeScanner.vue";
|
||||||
import GenericButton from "@/components/viewport/page/templates/generic/graphics/GenericButton.vue";
|
import GenericButton from "@/components/viewport/page/templates/generic/graphics/GenericButton.vue";
|
||||||
@@ -103,9 +110,7 @@ const createRegistrationAutosave = (vehicleIndex: number) =>
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SessionUser.objects.orders.set[`reg_${vehicleIndex}`](normalizedOrderId, value);
|
return saveOrderMetadataField(`reg_${vehicleIndex}`, value, normalizedOrderId);
|
||||||
const refreshedOrder = await getOrderDetails(normalizedOrderId);
|
|
||||||
return refreshedOrder?.[`reg_${vehicleIndex}`] ?? value;
|
|
||||||
},
|
},
|
||||||
onSaved: async (value) => {
|
onSaved: async (value) => {
|
||||||
setPersistedRegistrationValue(vehicleIndex, value);
|
setPersistedRegistrationValue(vehicleIndex, value);
|
||||||
@@ -194,6 +199,11 @@ const flushAllRegistrations = async () => {
|
|||||||
await Promise.all([1, 2, 3].map((vehicleIndex) => registrationAutosaves[vehicleIndex].flush()));
|
await Promise.all([1, 2, 3].map((vehicleIndex) => registrationAutosaves[vehicleIndex].flush()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const waitForAllRegistrationSaves = async () => {
|
||||||
|
await Promise.all([1, 2, 3].map((vehicleIndex) => registrationAutosaves[vehicleIndex].flushAndWait()));
|
||||||
|
};
|
||||||
|
const unregisterStepSaveBarrier = registerPosStepSaveBarrier(waitForAllRegistrationSaves);
|
||||||
|
|
||||||
// Function to set a value if it is not null
|
// Function to set a value if it is not null
|
||||||
const createVehicleSelectionFromSearchResult = (value: PosSearchResult) => ({
|
const createVehicleSelectionFromSearchResult = (value: PosSearchResult) => ({
|
||||||
...value,
|
...value,
|
||||||
@@ -266,11 +276,12 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
unregisterStepSaveBarrier();
|
||||||
void flushAllRegistrations();
|
void flushAllRegistrations();
|
||||||
});
|
});
|
||||||
|
|
||||||
const closeManualInput = async () => {
|
const closeManualInput = async () => {
|
||||||
await flushAllRegistrations();
|
await waitForAllRegistrationSaves();
|
||||||
emit("close");
|
emit("close");
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+2
-1
@@ -12,6 +12,7 @@ import PosDepartmentStepMobileButtonNextStep from "@/components/displays/departm
|
|||||||
import SessionUser from "@/components/session/token/SessionUser.vue";
|
import SessionUser from "@/components/session/token/SessionUser.vue";
|
||||||
import { PosOrder } from "@/components/displays/department/pos/steps/mobile/objects/PosOrder.vue";
|
import { PosOrder } from "@/components/displays/department/pos/steps/mobile/objects/PosOrder.vue";
|
||||||
import WhiteBoxCard from "@/components/displays/boxes/WhiteBoxCard.vue";
|
import WhiteBoxCard from "@/components/displays/boxes/WhiteBoxCard.vue";
|
||||||
|
import { todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
// Define the close event to emit when the component is closed
|
// Define the close event to emit when the component is closed
|
||||||
const emit = defineEmits(["close"]);
|
const emit = defineEmits(["close"]);
|
||||||
/** Display variables */
|
/** Display variables */
|
||||||
@@ -126,7 +127,7 @@ const liveTransactions = ref(null);
|
|||||||
const isLoading = ref(true);
|
const isLoading = ref(true);
|
||||||
|
|
||||||
const syncListTransactionHistory = async () => {
|
const syncListTransactionHistory = async () => {
|
||||||
let dateToday = new Date().toISOString().split("T")[0]; // Get today's date in YYYY-MM-DD format
|
let dateToday = todayLocalDateOnly(); // Get today's date in YYYY-MM-DD format
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
// Fetch the list of orders created today for the current department
|
// Fetch the list of orders created today for the current department
|
||||||
SessionUser.objects.orders.get
|
SessionUser.objects.orders.get
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const props = defineProps({
|
|||||||
import {computed, ref, watch} from 'vue';
|
import {computed, ref, watch} from 'vue';
|
||||||
import { departments, getDepartments, getDepartmentName} from "@/components/pagination/departmentTabs.vue";
|
import { departments, getDepartments, getDepartmentName} from "@/components/pagination/departmentTabs.vue";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import {orderBy, orderDirection, setOrder, loadList} from "@/components/pagination/paginatedList.vue";
|
import { usePaginatedListInstance } from "@/components/pagination/paginatedList.vue";
|
||||||
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
|
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import ActionSettingsWheelItemLabel from "@/components/displays/buttons/ActionSettingsWheelItemLabel.vue";
|
import ActionSettingsWheelItemLabel from "@/components/displays/buttons/ActionSettingsWheelItemLabel.vue";
|
||||||
@@ -38,6 +38,8 @@ import {
|
|||||||
setCachedXlvaskUsageAmount
|
setCachedXlvaskUsageAmount
|
||||||
} from "@/components/displays/department/pos/sync/xlvaskUsageAmountCache.js";
|
} from "@/components/displays/department/pos/sync/xlvaskUsageAmountCache.js";
|
||||||
|
|
||||||
|
const { orderBy, orderDirection, setOrder, loadList } = usePaginatedListInstance();
|
||||||
|
|
||||||
const redirectDepartmentOrderPage = (orderId, departmentId) => {
|
const redirectDepartmentOrderPage = (orderId, departmentId) => {
|
||||||
// Send the user to the order page (In a new tab)
|
// Send the user to the order page (In a new tab)
|
||||||
// `/admin/${departmentId}/modules/pos/orders/${orderId}`
|
// `/admin/${departmentId}/modules/pos/orders/${orderId}`
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export const SELFWASH_PERIOD_ALL_LIMIT = 10000;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { formatLocalDateOnly } from '@/services/dateOnly.js';
|
||||||
type startEndDate = {
|
type startEndDate = {
|
||||||
start: Date; // The start date of the range
|
start: Date; // The start date of the range
|
||||||
end: Date; // The end date of the range
|
end: Date; // The end date of the range
|
||||||
@@ -27,7 +28,7 @@ const datePresetFunctions = {
|
|||||||
month: {
|
month: {
|
||||||
// Get the first day of the month based on the provided date first day at 00:00:01
|
// Get the first day of the month based on the provided date first day at 00:00:01
|
||||||
firstDayOfMonth: (date: Date): Date => {
|
firstDayOfMonth: (date: Date): Date => {
|
||||||
return new Date(date.getFullYear(), date.getMonth(), 2, 0, 0, 1, 0); // Set to the first day of the month at 00:00:01
|
return new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0); // Set to the first day of the month at 00:00:00
|
||||||
},
|
},
|
||||||
// Get the last day of the month based on the provided date last day at 23:59:59
|
// Get the last day of the month based on the provided date last day at 23:59:59
|
||||||
lastDayOfMonth: (date: Date): Date => {
|
lastDayOfMonth: (date: Date): Date => {
|
||||||
@@ -116,7 +117,7 @@ export const datePresets = <datePreset[]>[
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const convertToISO = (date: Date): string => {
|
const convertToISO = (date: Date): string => {
|
||||||
return date.toISOString().split('T')[0]; // Convert to YYYY-MM-DD format
|
return formatLocalDateOnly(date); // Convert to YYYY-MM-DD format
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dateFunctions = {
|
export const dateFunctions = {
|
||||||
@@ -150,4 +151,4 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template></template>
|
<template></template>
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ if (router.currentRoute.value.params.departmentId) {
|
|||||||
<span>{{ t('common.add') }} {{ SessionUser.objects.department_notification_sms.meta.labels.single }}</span>
|
<span>{{ t('common.add') }} {{ SessionUser.objects.department_notification_sms.meta.labels.single }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<DepartmentNotificationsPhoneTable :objects="list" />
|
<DepartmentNotificationsPhoneTable :objects="list" :load-list="loadList" />
|
||||||
</TableLabeledPagination>
|
</TableLabeledPagination>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,32 @@ const props = defineProps({
|
|||||||
type: Number, default: 0
|
type: Number, default: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
import {computed, ref, watch } from "vue";
|
import {computed, provide, ref, watch } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import {
|
import {
|
||||||
|
PaginatedListKey,
|
||||||
|
usePaginatedList,
|
||||||
|
} from "@/components/pagination/paginatedList.vue";
|
||||||
|
import PaginationNavigation from "@/components/displays/pagination/PaginationNavigation.vue";
|
||||||
|
|
||||||
|
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
|
||||||
|
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
|
||||||
|
import XlvaskUsageOrdersTable from "@/components/displays/department/pos/sync/xlvaskUsageOrdersTable.vue";
|
||||||
|
import PaginationDisplayTemplateDates
|
||||||
|
from "@/components/displays/pagination/templates/PaginationDisplayTemplateDates.vue";
|
||||||
|
import ShowErrorField from "@/components/global/ShowErrorField.vue";
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { BSwitch } from "buefy";
|
||||||
|
import { isUsageOrderAttachedToOrder } from "@/components/displays/department/pos/sync/xlvaskUsageFilters.js";
|
||||||
|
import { SELFWASH_PERIOD_ALL_LIMIT } from "@/components/displays/department/pos/sync/xlvaskUsagePeriodConstants.js";
|
||||||
|
import { formatLocalDateOnly, parseLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const router = useRouter();
|
||||||
|
const paginatedList = usePaginatedList();
|
||||||
|
provide(PaginatedListKey, paginatedList);
|
||||||
|
|
||||||
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
list,
|
list,
|
||||||
loadList,
|
loadList,
|
||||||
@@ -38,22 +61,8 @@ import {
|
|||||||
setOrder,
|
setOrder,
|
||||||
hideSearchField,
|
hideSearchField,
|
||||||
setHideSearchField,
|
setHideSearchField,
|
||||||
} from "@/components/pagination/paginatedList.vue";
|
} = paginatedList;
|
||||||
import PaginationNavigation from "@/components/displays/pagination/PaginationNavigation.vue";
|
|
||||||
|
|
||||||
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
|
|
||||||
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
|
|
||||||
import XlvaskUsageOrdersTable from "@/components/displays/department/pos/sync/xlvaskUsageOrdersTable.vue";
|
|
||||||
import PaginationDisplayTemplateDates
|
|
||||||
from "@/components/displays/pagination/templates/PaginationDisplayTemplateDates.vue";
|
|
||||||
import ShowErrorField from "@/components/global/ShowErrorField.vue";
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { BSwitch } from "buefy";
|
|
||||||
import { isUsageOrderAttachedToOrder } from "@/components/displays/department/pos/sync/xlvaskUsageFilters.js";
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const router = useRouter();
|
|
||||||
const SELFWASH_PERIOD_ALL_LIMIT = 10000;
|
|
||||||
setEndpoint("/modules/xlvask/services/usage/orders", false);
|
setEndpoint("/modules/xlvask/services/usage/orders", false);
|
||||||
if (props.loadAllAtOnce) {
|
if (props.loadAllAtOnce) {
|
||||||
setMetaItemsPerPage(SELFWASH_PERIOD_ALL_LIMIT, false);
|
setMetaItemsPerPage(SELFWASH_PERIOD_ALL_LIMIT, false);
|
||||||
@@ -63,7 +72,7 @@ const parseInitialDate = (value) => {
|
|||||||
return new Date();
|
return new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = new Date(`${value}T00:00:00`);
|
const parsed = parseLocalDateOnly(value);
|
||||||
return Number.isNaN(parsed.getTime()) ? new Date() : parsed;
|
return Number.isNaN(parsed.getTime()) ? new Date() : parsed;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,7 +138,7 @@ const dateFrom = ref(parseInitialDate(props.initialDateFrom));
|
|||||||
const dateTo = ref(parseInitialDate(props.initialDateTo));
|
const dateTo = ref(parseInitialDate(props.initialDateTo));
|
||||||
|
|
||||||
const parsedDate = (date) => {
|
const parsedDate = (date) => {
|
||||||
return new Date(date);
|
return parseLocalDateOnly(date);
|
||||||
};
|
};
|
||||||
|
|
||||||
const reloadScheduled = ref(false);
|
const reloadScheduled = ref(false);
|
||||||
@@ -148,14 +157,14 @@ const actions = {
|
|||||||
from: {
|
from: {
|
||||||
select: (date) => {
|
select: (date) => {
|
||||||
dateFrom.value = parsedDate(date);
|
dateFrom.value = parsedDate(date);
|
||||||
setFilter("StartTime-date_from", date.toISOString().split("T")[0], false);
|
setFilter("StartTime-date_from", formatLocalDateOnly(date), false);
|
||||||
scheduleReload();
|
scheduleReload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
select: (date) => {
|
select: (date) => {
|
||||||
dateTo.value = parsedDate(date);
|
dateTo.value = parsedDate(date);
|
||||||
setFilter("StartTime-date_to", date.toISOString().split("T")[0], false);
|
setFilter("StartTime-date_to", formatLocalDateOnly(date), false);
|
||||||
scheduleReload();
|
scheduleReload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-4
@@ -109,8 +109,8 @@ import OrdersTable from "@/components/displays/department/pos/orders/ordersTable
|
|||||||
import {SessionUser} from "@/components/session/token/SessionUser.vue";
|
import {SessionUser} from "@/components/session/token/SessionUser.vue";
|
||||||
import PaginationDisplayFilters from "@/components/displays/pagination/PaginationDisplayFilters.vue";
|
import PaginationDisplayFilters from "@/components/displays/pagination/PaginationDisplayFilters.vue";
|
||||||
import DatePeriodSelector from "@/components/displays/buttons/DatePeriodSelector.vue";
|
import DatePeriodSelector from "@/components/displays/buttons/DatePeriodSelector.vue";
|
||||||
import {now} from "@vueuse/core";
|
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { formatLocalDateOnly, parseLocalDateOnly, todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -176,8 +176,8 @@ if (props.autoLoad) {
|
|||||||
const date_from = ref(null);
|
const date_from = ref(null);
|
||||||
const date_to = ref(null);
|
const date_to = ref(null);
|
||||||
const onDateRangeSelected = (newSelectionStartDate, newSelectionToDate) => {
|
const onDateRangeSelected = (newSelectionStartDate, newSelectionToDate) => {
|
||||||
const formattedStartDate = newSelectionStartDate.toISOString().split("T")[0];
|
const formattedStartDate = formatLocalDateOnly(newSelectionStartDate);
|
||||||
const formattedEndDate = newSelectionToDate.toISOString().split("T")[0];
|
const formattedEndDate = formatLocalDateOnly(newSelectionToDate);
|
||||||
date_from.value = formattedStartDate;
|
date_from.value = formattedStartDate;
|
||||||
date_to.value = formattedEndDate;
|
date_to.value = formattedEndDate;
|
||||||
setFilter("created_at-date_from", formattedStartDate, true);
|
setFilter("created_at-date_from", formattedStartDate, true);
|
||||||
@@ -361,7 +361,7 @@ const doesEndpointMatch = (matcher) => {
|
|||||||
<!-- Shortcuts for date filters -->
|
<!-- Shortcuts for date filters -->
|
||||||
<DatePeriodSelector :on-selection-change="onDateRangeSelected"
|
<DatePeriodSelector :on-selection-change="onDateRangeSelected"
|
||||||
:visibility="{ showDailySelector: false, showWeeklySelector: false, showMultipleMonthWarning: false, showUpdateButton: false, showMonthSelector: false, showStartDate: false, showEndDate: false, showSelectionValidity: false, showYearSelector: false, showToLabel: false }"
|
:visibility="{ showDailySelector: false, showWeeklySelector: false, showMultipleMonthWarning: false, showUpdateButton: false, showMonthSelector: false, showStartDate: false, showEndDate: false, showSelectionValidity: false, showYearSelector: false, showToLabel: false }"
|
||||||
v-bind:selection="{ startDate: date_from ? new Date(date_from) : new Date(now()), endDate: date_to ? new Date(date_to) : new Date(now()) }"/>
|
v-bind:selection="{ startDate: parseLocalDateOnly(date_from || todayLocalDateOnly()), endDate: parseLocalDateOnly(date_to || todayLocalDateOnly()) }"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
|
|||||||
@@ -10,11 +10,21 @@ const props = defineProps(["setCustomerFilter", "hideSearch", "autoLoad"]);
|
|||||||
|
|
||||||
const paginatedList = usePaginatedList();
|
const paginatedList = usePaginatedList();
|
||||||
provide(PaginatedListKey, paginatedList);
|
provide(PaginatedListKey, paginatedList);
|
||||||
const { list, loadList, setEndpoint, setFilter, setOrder, hideSearchField, setHideSearchField } = paginatedList;
|
const {
|
||||||
|
list,
|
||||||
|
loadList,
|
||||||
|
setAdditionalQueryParameters,
|
||||||
|
setEndpoint,
|
||||||
|
setFilter,
|
||||||
|
setOrder,
|
||||||
|
hideSearchField,
|
||||||
|
setHideSearchField,
|
||||||
|
} = paginatedList;
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
setEndpoint("/users", false);
|
setEndpoint("/users", false);
|
||||||
setFilter("customer_number", 0, false);
|
setFilter("customer_number", 0, false);
|
||||||
|
setAdditionalQueryParameters({ include_limited_backoffice_employees: "true" });
|
||||||
setOrder("created_at", "desc");
|
setOrder("created_at", "desc");
|
||||||
|
|
||||||
// Hide the search field, if the hideSearch prop is set
|
// Hide the search field, if the hideSearch prop is set
|
||||||
|
|||||||
@@ -24,13 +24,16 @@ import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue"
|
|||||||
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
|
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
|
||||||
import BookingsTable from "@/components/displays/user/bookings/bookingsTable.vue";
|
import BookingsTable from "@/components/displays/user/bookings/bookingsTable.vue";
|
||||||
import {departments} from "@/components/pagination/departmentTabs.vue";
|
import {departments} from "@/components/pagination/departmentTabs.vue";
|
||||||
import {ref, watch} from "vue";
|
import {computed, ref, watch} from "vue";
|
||||||
import { Colors } from "@/ThemeConfig.vue";
|
import { Colors } from "@/ThemeConfig.vue";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const isUserRoute = computed(() => router.currentRoute.value.path.startsWith("/user"));
|
||||||
|
const canCreateBooking = computed(() => SessionUser.canAccessCustomerFeature("bookings", "add"));
|
||||||
|
|
||||||
// Set the selected status to all
|
// Set the selected status to all
|
||||||
const selectedStatus = ref("*");
|
const selectedStatus = ref("*");
|
||||||
@@ -52,7 +55,7 @@ watch(() => getFilter("status"), (value) => {
|
|||||||
// If the route starts with /user, set the endpoint to /user/bookings
|
// If the route starts with /user, set the endpoint to /user/bookings
|
||||||
if (router.currentRoute.value.path.startsWith("/user")) {
|
if (router.currentRoute.value.path.startsWith("/user")) {
|
||||||
setEndpoint("/user/bookings", false);
|
setEndpoint("/user/bookings", false);
|
||||||
setFilter("date", new Date().toISOString().split("T")[0], false);
|
setFilter("date", todayLocalDateOnly(), false);
|
||||||
setOrder("created_at", "desc");
|
setOrder("created_at", "desc");
|
||||||
}
|
}
|
||||||
// If the route starts with /superuser, set the endpoint to /bookings
|
// If the route starts with /superuser, set the endpoint to /bookings
|
||||||
@@ -81,7 +84,7 @@ loadList();
|
|||||||
const showingToday = ref(true);
|
const showingToday = ref(true);
|
||||||
|
|
||||||
watch(() => getFilter("date"), (value) => {
|
watch(() => getFilter("date"), (value) => {
|
||||||
showingToday.value = value === new Date().toISOString().split("T")[0];
|
showingToday.value = value === todayLocalDateOnly();
|
||||||
});
|
});
|
||||||
|
|
||||||
// If the screen is mobile, set the is small variable to true
|
// If the screen is mobile, set the is small variable to true
|
||||||
@@ -147,17 +150,17 @@ const showNewOrderBookingsPortal = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="column is-auto-fill my-3" v-if="!isSmall"/>
|
<div class="column is-auto-fill my-3" v-if="!isSmall"/>
|
||||||
<!-- Only show the bookings for today (Switch, if the route is /user) -->
|
<!-- Only show the bookings for today (Switch, if the route is /user) -->
|
||||||
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
|
<div class="column is-narrow my-3" v-if="isUserRoute" :class="{ 'has-text-right': !isSmall }">
|
||||||
<label class="label">{{ isSmall ? t('common.today') : t('pagination.show_only_today') }}</label>
|
<label class="label">{{ isSmall ? t('common.today') : t('pagination.show_only_today') }}</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input id="today" type="checkbox" class="switch is-rounded" @change="(event) => { setFilter('date', event.target.checked ? new Date().toISOString().split('T')[0] : '*') }" checked="checked" :class="{ 'is-link': showingToday }" />
|
<input id="today" type="checkbox" class="switch is-rounded" @change="(event) => { setFilter('date', event.target.checked ? todayLocalDateOnly() : '*') }" checked="checked" :class="{ 'is-link': showingToday }" />
|
||||||
<label for="today"></label>
|
<label for="today"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Create a new booking, if the route is /user -->
|
<!-- Create a new booking, if the route is /user -->
|
||||||
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
|
<div class="column is-narrow my-3" v-if="isUserRoute && canCreateBooking" :class="{ 'has-text-right': !isSmall }">
|
||||||
<button
|
<button
|
||||||
class="button is-link"
|
class="button is-link"
|
||||||
@click="SessionUser.functions.redirectTo.external('/user/bookings/new', true)"
|
@click="SessionUser.functions.redirectTo.external('/user/bookings/new', true)"
|
||||||
@@ -188,4 +191,4 @@ const showNewOrderBookingsPortal = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import OrderBookingsTable from "@/views/dashboards/userDashboard/bookings/displa
|
|||||||
import { departments, getDepartments } from "@/components/pagination/departmentTabs.vue";
|
import { departments, getDepartments } from "@/components/pagination/departmentTabs.vue";
|
||||||
import { Colors } from "@/ThemeConfig.vue";
|
import { Colors } from "@/ThemeConfig.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { endOfLocalDate, startOfLocalDate, todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
/**
|
/**
|
||||||
@@ -16,6 +17,7 @@ const { t } = useI18n();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isUserRoute = computed(() => router.currentRoute.value.path.startsWith("/user"));
|
const isUserRoute = computed(() => router.currentRoute.value.path.startsWith("/user"));
|
||||||
const isAdminRoute = computed(() => router.currentRoute.value.path.startsWith("/admin"));
|
const isAdminRoute = computed(() => router.currentRoute.value.path.startsWith("/admin"));
|
||||||
|
const canCreateBooking = computed(() => SessionUser.canAccessCustomerFeature("bookings", "add"));
|
||||||
/**
|
/**
|
||||||
* Props
|
* Props
|
||||||
*/
|
*/
|
||||||
@@ -52,11 +54,9 @@ const onOnlyTodayFilterChange = (event, autoLoadList = true) => {
|
|||||||
setFilter("datetime-date_from", null, false);
|
setFilter("datetime-date_from", null, false);
|
||||||
setFilter("datetime-date_to", null, false);
|
setFilter("datetime-date_to", null, false);
|
||||||
} else {
|
} else {
|
||||||
const startOfDay = new Date().setHours(0, 0, 0, 0);
|
|
||||||
const endOfDay = new Date().setHours(23, 59, 59, 999);
|
|
||||||
//setFilter('datetime', null, false);
|
//setFilter('datetime', null, false);
|
||||||
setFilter("datetime-date_from", new Date(startOfDay).toISOString(), false);
|
setFilter("datetime-date_from", startOfLocalDate(val).toISOString(), false);
|
||||||
setFilter("datetime-date_to", new Date(endOfDay).toISOString(), false);
|
setFilter("datetime-date_to", endOfLocalDate(val).toISOString(), false);
|
||||||
}
|
}
|
||||||
if (autoLoadList) {
|
if (autoLoadList) {
|
||||||
loadList();
|
loadList();
|
||||||
@@ -95,7 +95,7 @@ onMounted(() => {
|
|||||||
departmentFilter.value = value;
|
departmentFilter.value = value;
|
||||||
} else if (key === "only_today" && value === true) {
|
} else if (key === "only_today" && value === true) {
|
||||||
setFilterKey = false; // Since the only_today filter is handled separately
|
setFilterKey = false; // Since the only_today filter is handled separately
|
||||||
onOnlyTodayFilterChange({ target: { value: new Date().toISOString().split("T")[0] } }, false);
|
onOnlyTodayFilterChange({ target: { value: todayLocalDateOnly() } }, false);
|
||||||
}
|
}
|
||||||
if (setFilterKey) {
|
if (setFilterKey) {
|
||||||
setFilter(key, value, false);
|
setFilter(key, value, false);
|
||||||
@@ -170,7 +170,7 @@ onMounted(() => {
|
|||||||
<div class="select">
|
<div class="select">
|
||||||
<select :value="onlyTodayFilter" @change="onOnlyTodayFilterChange">
|
<select :value="onlyTodayFilter" @change="onOnlyTodayFilterChange">
|
||||||
<option value="*">{{ t("common.all") }}</option>
|
<option value="*">{{ t("common.all") }}</option>
|
||||||
<option :value="new Date().toISOString().split('T')[0]">{{ t("common.yes") }}</option>
|
<option :value="todayLocalDateOnly()">{{ t("common.yes") }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -193,7 +193,7 @@ onMounted(() => {
|
|||||||
@change="
|
@change="
|
||||||
(event) => {
|
(event) => {
|
||||||
onOnlyTodayFilterChange({
|
onOnlyTodayFilterChange({
|
||||||
target: { value: event.target.checked ? new Date().toISOString().split('T')[0] : '*' },
|
target: { value: event.target.checked ? todayLocalDateOnly() : '*' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@@ -203,7 +203,7 @@ onMounted(() => {
|
|||||||
<label for="today"></label>
|
<label for="today"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="order-bookings-pagination__new-booking-action">
|
<div class="order-bookings-pagination__new-booking-action" v-if="canCreateBooking">
|
||||||
<label class="label is-small order-bookings-pagination__desktop-spacer"> </label>
|
<label class="label is-small order-bookings-pagination__desktop-spacer"> </label>
|
||||||
<button
|
<button
|
||||||
class="button is-link button-same-width"
|
class="button is-link button-same-width"
|
||||||
@@ -226,7 +226,7 @@ onMounted(() => {
|
|||||||
@change="
|
@change="
|
||||||
(event) => {
|
(event) => {
|
||||||
onOnlyTodayFilterChange(
|
onOnlyTodayFilterChange(
|
||||||
{ target: { value: event.target.checked ? new Date().toISOString().split('T')[0] : '*' } },
|
{ target: { value: event.target.checked ? todayLocalDateOnly() : '*' } },
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
onOrderIdFilterChange({ target: { value: event.target.checked ? 'is null' : '*' } });
|
onOrderIdFilterChange({ target: { value: event.target.checked ? 'is null' : '*' } });
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import {SessionUser} from "@/components/session/token/SessionUser.vue";
|
import {SessionUser} from "@/components/session/token/SessionUser.vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import VehiclesTable from "@/components/displays/user/vehicles/vehiclesTable.vue";
|
import VehiclesTable from "@/components/displays/user/vehicles/vehiclesTable.vue";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
import PaginationDisplayFilters from "@/components/displays/pagination/PaginationDisplayFilters.vue";
|
import PaginationDisplayFilters from "@/components/displays/pagination/PaginationDisplayFilters.vue";
|
||||||
import {Colors} from "@/ThemeConfig.vue";
|
import {Colors} from "@/ThemeConfig.vue";
|
||||||
@@ -20,6 +21,8 @@ const { t } = useI18n();
|
|||||||
* Router
|
* Router
|
||||||
*/
|
*/
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const isUserRoute = computed(() => router.currentRoute.value.path.startsWith("/user"));
|
||||||
|
const canAddVehicle = computed(() => SessionUser.canAccessCustomerFeature("vehicles", "add"));
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
customer_id: {
|
customer_id: {
|
||||||
@@ -52,9 +55,13 @@ loadList();
|
|||||||
:columnLabels="{ type: SessionUser.objects.vehicles.columns.type.label }"
|
:columnLabels="{ type: SessionUser.objects.vehicles.columns.type.label }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="$slots.actions" class="vehicles-pagination__add-action">
|
||||||
|
<label class="label is-small"> </label>
|
||||||
|
<slot name="actions" />
|
||||||
|
</div>
|
||||||
<!-- Create a new vehicle, if the route is /user -->
|
<!-- Create a new vehicle, if the route is /user -->
|
||||||
<div class="vehicles-pagination__add-action"
|
<div class="vehicles-pagination__add-action"
|
||||||
v-if="router.currentRoute.value.path.startsWith('/user')">
|
v-if="isUserRoute && canAddVehicle">
|
||||||
<label class="label is-small"> </label>
|
<label class="label is-small"> </label>
|
||||||
<button
|
<button
|
||||||
class="button is-link button-same-width vehicles-pagination__add-button"
|
class="button is-link button-same-width vehicles-pagination__add-button"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PaginationDisplayItemColumn from "@/components/displays/pagination/PaginationDisplayItemColumn.vue";
|
import PaginationDisplayItemColumn from "@/components/displays/pagination/PaginationDisplayItemColumn.vue";
|
||||||
import { ref, computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { formatLocalDateOnly, parseLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -19,7 +20,7 @@ defineExpose({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const formattedDate = computed(() => {
|
const formattedDate = computed(() => {
|
||||||
return date.value.toISOString().split('T')[0];
|
return formatLocalDateOnly(date.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// This component is used to display a date input in a pagination display item column.
|
// This component is used to display a date input in a pagination display item column.
|
||||||
@@ -36,8 +37,7 @@ const formattedDate = computed(() => {
|
|||||||
class="input"
|
class="input"
|
||||||
:value="formattedDate"
|
:value="formattedDate"
|
||||||
@input="(e) => {
|
@input="(e) => {
|
||||||
const newDate = new Date(e.target.value);
|
const newDate = parseLocalDateOnly(e.target.value);
|
||||||
console.log('Selected date:', newDate);
|
|
||||||
emit('update:date', newDate);
|
emit('update:date', newDate);
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
@@ -47,4 +47,4 @@ const formattedDate = computed(() => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { SessionUser } from '@/components/session/token/SessionUser.vue';
|
import { SessionUser } from '@/components/session/token/SessionUser.vue';
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ import PaginationDisplayTemplateButton
|
|||||||
from "@/components/displays/pagination/templates/PaginationDisplayTemplateButton.vue";
|
from "@/components/displays/pagination/templates/PaginationDisplayTemplateButton.vue";
|
||||||
import { datePresets , dateFunctions} from '@/components/displays/pagination/PaginationDisplayDates.vue';
|
import { datePresets , dateFunctions} from '@/components/displays/pagination/PaginationDisplayDates.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
defineProps({
|
||||||
startDate: {
|
startDate: {
|
||||||
type: Date,
|
type: Date,
|
||||||
required: true,
|
required: true,
|
||||||
@@ -46,6 +45,7 @@ function updateStartDate(date: Date) {
|
|||||||
// Update the end date model when the date is changed
|
// Update the end date model when the date is changed
|
||||||
function updateEndDate(date: Date) {
|
function updateEndDate(date: Date) {
|
||||||
endDateModel.value = date;
|
endDateModel.value = date;
|
||||||
|
emits('update:endDate', date);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -79,4 +79,4 @@ function updateEndDate(date: Date) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
import { RouterLink } from "vue-router";
|
import { RouterLink } from "vue-router";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import {
|
import {
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
const { t, te, locale } = useI18n();
|
const { t, te, locale } = useI18n();
|
||||||
|
|
||||||
const MAX_GATEWAY_CARDS = 8;
|
const MAX_GATEWAY_CARDS = 8;
|
||||||
|
const DEFAULT_DASHBOARD_TAB = "infrastructure";
|
||||||
const gatewayStatusPriority = Object.freeze({
|
const gatewayStatusPriority = Object.freeze({
|
||||||
OFFLINE: 0,
|
OFFLINE: 0,
|
||||||
DEGRADED: 1,
|
DEGRADED: 1,
|
||||||
@@ -77,7 +78,14 @@ const gatewayFleetUsage = ref(createEmptyGatewayFleetUsage());
|
|||||||
const gatewaySectionSuppressed = ref(false);
|
const gatewaySectionSuppressed = ref(false);
|
||||||
const gatewayDepartments = ref({});
|
const gatewayDepartments = ref({});
|
||||||
const gatewayDepartmentsLoaded = ref(false);
|
const gatewayDepartmentsLoaded = ref(false);
|
||||||
|
const activeDashboardTab = ref(DEFAULT_DASHBOARD_TAB);
|
||||||
const showGatewaySection = computed(() => canViewGateways.value && !gatewaySectionSuppressed.value);
|
const showGatewaySection = computed(() => canViewGateways.value && !gatewaySectionSuppressed.value);
|
||||||
|
const dashboardTabKeys = computed(() => [
|
||||||
|
DEFAULT_DASHBOARD_TAB,
|
||||||
|
...(showGatewaySection.value ? ["gateways"] : []),
|
||||||
|
"modules",
|
||||||
|
"sessions",
|
||||||
|
]);
|
||||||
const gatewaySummaryCards = computed(() => [
|
const gatewaySummaryCards = computed(() => [
|
||||||
{
|
{
|
||||||
id: "total",
|
id: "total",
|
||||||
@@ -187,6 +195,16 @@ const isStale = computed(() => {
|
|||||||
return nowTick.value - lastLoadedAt.value.getTime() > (refreshAfterSeconds.value * 2000);
|
return nowTick.value - lastLoadedAt.value.getTime() > (refreshAfterSeconds.value * 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [activeDashboardTab.value, dashboardTabKeys.value.join("|")],
|
||||||
|
() => {
|
||||||
|
if (!dashboardTabKeys.value.includes(activeDashboardTab.value)) {
|
||||||
|
activeDashboardTab.value = DEFAULT_DASHBOARD_TAB;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
const loadStatus = async ({ force = false } = {}) => {
|
const loadStatus = async ({ force = false } = {}) => {
|
||||||
const [snapshotValue] = await Promise.all([
|
const [snapshotValue] = await Promise.all([
|
||||||
getSuperuserSystemStatus({ force }),
|
getSuperuserSystemStatus({ force }),
|
||||||
@@ -707,203 +725,250 @@ function modulePath(key) {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="system-status-section">
|
<b-tabs
|
||||||
<div class="section-heading">
|
v-model="activeDashboardTab"
|
||||||
<h3>{{ $t("system_status.sections.infrastructure") }}</h3>
|
expanded
|
||||||
<RouterLink class="section-link" to="/superuser/system/replication">
|
type="is-boxed"
|
||||||
{{ $t("system_status.actions.open_replication") }}
|
class="system-status-tabs"
|
||||||
</RouterLink>
|
data-testid="system-status-dashboard-tabs"
|
||||||
</div>
|
|
||||||
<div class="status-card-grid">
|
|
||||||
<article
|
|
||||||
v-for="card in statusCards"
|
|
||||||
:key="card.id"
|
|
||||||
class="status-card"
|
|
||||||
:class="statusClass(card.status)"
|
|
||||||
:data-testid="`status-card-${card.id}`"
|
|
||||||
>
|
|
||||||
<div class="status-card__top">
|
|
||||||
<p class="status-card__title">{{ card.title }}</p>
|
|
||||||
<span class="status-pill" :class="statusClass(card.status)">{{ statusLabel(card.status) }}</span>
|
|
||||||
</div>
|
|
||||||
<strong class="status-card__primary">{{ card.primary }}</strong>
|
|
||||||
<p class="status-card__secondary">{{ card.secondary }}</p>
|
|
||||||
<small class="status-card__detail">{{ card.detail }}</small>
|
|
||||||
<small
|
|
||||||
v-if="card.replicationLabel"
|
|
||||||
class="status-card__detail status-card__replication"
|
|
||||||
:data-testid="`status-card-${card.id}-replication`"
|
|
||||||
>
|
|
||||||
{{ card.replicationLabel }}
|
|
||||||
</small>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section
|
|
||||||
v-if="showGatewaySection"
|
|
||||||
class="system-status-section"
|
|
||||||
data-testid="system-status-gateways"
|
|
||||||
>
|
>
|
||||||
<div class="section-heading">
|
<b-tab-item value="infrastructure">
|
||||||
<div>
|
<template #header>
|
||||||
<h3>{{ $t("system_status.sections.gateways") }}</h3>
|
<b-icon pack="fas" icon="server" />
|
||||||
<p class="section-subtitle">{{ $t("system_status.gateways.description") }}</p>
|
<span data-testid="system-status-tab-infrastructure">
|
||||||
</div>
|
{{ $t("system_status.sections.infrastructure") }}
|
||||||
<RouterLink class="section-link" to="/superuser/selfserve/edge-agents">
|
</span>
|
||||||
{{ $t("system_status.gateways.actions.open_fleet") }}
|
</template>
|
||||||
</RouterLink>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gateway-summary-grid">
|
<section class="system-status-section" data-testid="system-status-panel-infrastructure">
|
||||||
<article
|
<div class="section-heading">
|
||||||
v-for="card in gatewaySummaryCards"
|
<h3>{{ $t("system_status.sections.infrastructure") }}</h3>
|
||||||
:key="card.id"
|
<RouterLink class="section-link" to="/superuser/system/replication">
|
||||||
class="summary-card gateway-summary-card"
|
{{ $t("system_status.actions.open_replication") }}
|
||||||
:class="card.toneClass"
|
</RouterLink>
|
||||||
:data-testid="`gateway-summary-card-${card.id}`"
|
</div>
|
||||||
|
<div class="status-card-grid">
|
||||||
|
<article
|
||||||
|
v-for="card in statusCards"
|
||||||
|
:key="card.id"
|
||||||
|
class="status-card"
|
||||||
|
:class="statusClass(card.status)"
|
||||||
|
:data-testid="`status-card-${card.id}`"
|
||||||
|
>
|
||||||
|
<div class="status-card__top">
|
||||||
|
<p class="status-card__title">{{ card.title }}</p>
|
||||||
|
<span class="status-pill" :class="statusClass(card.status)">{{ statusLabel(card.status) }}</span>
|
||||||
|
</div>
|
||||||
|
<strong class="status-card__primary">{{ card.primary }}</strong>
|
||||||
|
<p class="status-card__secondary">{{ card.secondary }}</p>
|
||||||
|
<small class="status-card__detail">{{ card.detail }}</small>
|
||||||
|
<small
|
||||||
|
v-if="card.replicationLabel"
|
||||||
|
class="status-card__detail status-card__replication"
|
||||||
|
:data-testid="`status-card-${card.id}-replication`"
|
||||||
|
>
|
||||||
|
{{ card.replicationLabel }}
|
||||||
|
</small>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</b-tab-item>
|
||||||
|
|
||||||
|
<b-tab-item v-if="showGatewaySection" value="gateways">
|
||||||
|
<template #header>
|
||||||
|
<b-icon pack="fas" icon="network-wired" />
|
||||||
|
<span data-testid="system-status-tab-gateways">
|
||||||
|
{{ $t("system_status.sections.gateways") }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<section
|
||||||
|
class="system-status-section"
|
||||||
|
data-testid="system-status-gateways"
|
||||||
>
|
>
|
||||||
<p class="summary-label">{{ card.title }}</p>
|
<div class="section-heading">
|
||||||
<strong>{{ card.value }}</strong>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="gatewayError"
|
|
||||||
class="notification is-warning is-light gateway-notification"
|
|
||||||
data-testid="gateway-section-error"
|
|
||||||
>
|
|
||||||
{{ $t("system_status.gateways.error") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="gatewayLoading && !gatewayCards.length"
|
|
||||||
class="notification is-light gateway-notification"
|
|
||||||
>
|
|
||||||
{{ $t("system_status.gateways.loading") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="gatewayCards.length" class="gateway-grid">
|
|
||||||
<article
|
|
||||||
v-for="gateway in gatewayCards"
|
|
||||||
:key="gateway.id"
|
|
||||||
class="gateway-card"
|
|
||||||
:class="gateway.toneClass"
|
|
||||||
:data-testid="`gateway-card-${gateway.id}`"
|
|
||||||
>
|
|
||||||
<div class="gateway-card__top">
|
|
||||||
<div>
|
<div>
|
||||||
<strong class="gateway-card__title">{{ gateway.displayLabel }}</strong>
|
<h3>{{ $t("system_status.sections.gateways") }}</h3>
|
||||||
<p class="gateway-card__subtitle">{{ gateway.departmentName }}</p>
|
<p class="section-subtitle">{{ $t("system_status.gateways.description") }}</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="status-pill" :class="gateway.toneClass">{{ gateway.statusLabel }}</span>
|
<RouterLink class="section-link" to="/superuser/selfserve/edge-agents">
|
||||||
|
{{ $t("system_status.gateways.actions.open_fleet") }}
|
||||||
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gateway-card__meta">
|
<div class="gateway-summary-grid">
|
||||||
<span>
|
<article
|
||||||
{{ $t("system_status.gateways.labels.discovery") }}: {{ gateway.discoveryLabel }}
|
v-for="card in gatewaySummaryCards"
|
||||||
</span>
|
:key="card.id"
|
||||||
<span>
|
class="summary-card gateway-summary-card"
|
||||||
{{ $t("system_status.gateways.labels.last_heartbeat") }}: {{ gateway.lastHeartbeatLabel }}
|
:class="card.toneClass"
|
||||||
</span>
|
:data-testid="`gateway-summary-card-${card.id}`"
|
||||||
<span v-if="gateway.activeOperationLabel">
|
>
|
||||||
{{ $t("system_status.gateways.labels.active_operation") }}: {{ gateway.activeOperationLabel }}
|
<p class="summary-label">{{ card.title }}</p>
|
||||||
</span>
|
<strong>{{ card.value }}</strong>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="gateway.message" class="gateway-card__message">
|
<div
|
||||||
{{ gateway.message }}
|
v-if="gatewayError"
|
||||||
</p>
|
class="notification is-warning is-light gateway-notification"
|
||||||
|
data-testid="gateway-section-error"
|
||||||
<RouterLink :to="gateway.link" class="module-card__link">
|
>
|
||||||
{{ $t("system_status.gateways.actions.open_gateway") }}
|
{{ $t("system_status.gateways.error") }}
|
||||||
</RouterLink>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-else-if="!gatewayLoading && !gatewayError"
|
|
||||||
class="gateway-empty"
|
|
||||||
data-testid="gateway-empty-state"
|
|
||||||
>
|
|
||||||
{{ $t("system_status.gateways.empty") }}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="system-status-section">
|
|
||||||
<div class="section-heading">
|
|
||||||
<h3>{{ $t("system_status.sections.modules") }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="module-grid">
|
|
||||||
<article
|
|
||||||
v-for="module in modules"
|
|
||||||
:key="module.key"
|
|
||||||
class="module-card"
|
|
||||||
:class="statusClass(module.status)"
|
|
||||||
:data-testid="`module-card-${module.key}`"
|
|
||||||
>
|
|
||||||
<div class="module-card__top">
|
|
||||||
<p class="module-card__title">{{ moduleLabel(module.key) }}</p>
|
|
||||||
<span class="status-pill" :class="statusClass(module.status)">{{ statusLabel(module.status) }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="module-card__reason" :data-testid="`module-reason-${module.key}`">
|
|
||||||
{{ moduleReasonText(module) }}
|
<div
|
||||||
</p>
|
v-if="gatewayLoading && !gatewayCards.length"
|
||||||
<div class="module-card__meta">
|
class="notification is-light gateway-notification"
|
||||||
<span>{{ $t("system_status.labels.enabled") }}: {{ module.enabled ? $t("system_status.status.yes") : $t("system_status.status.no") }}</span>
|
>
|
||||||
<span>{{ $t("system_status.labels.configured") }}: {{ module.configured ? $t("system_status.status.yes") : $t("system_status.status.no") }}</span>
|
{{ $t("system_status.gateways.loading") }}
|
||||||
<span>{{ $t("system_status.labels.checked_at") }}: {{ formatDate(module.checked_at) }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<RouterLink v-if="modulePath(module.key)" :to="modulePath(module.key)" class="module-card__link">
|
|
||||||
{{ $t("system_status.actions.open_config") }}
|
|
||||||
</RouterLink>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="system-status-section">
|
<div v-if="gatewayCards.length" class="gateway-grid">
|
||||||
<div class="section-heading">
|
<article
|
||||||
<h3>{{ $t("system_status.sections.sessions") }}</h3>
|
v-for="gateway in gatewayCards"
|
||||||
<p>{{ $t("system_status.labels.activity_window", { minutes: sessions.active_window_minutes }) }}</p>
|
:key="gateway.id"
|
||||||
</div>
|
class="gateway-card"
|
||||||
|
:class="gateway.toneClass"
|
||||||
|
:data-testid="`gateway-card-${gateway.id}`"
|
||||||
|
>
|
||||||
|
<div class="gateway-card__top">
|
||||||
|
<div>
|
||||||
|
<strong class="gateway-card__title">{{ gateway.displayLabel }}</strong>
|
||||||
|
<p class="gateway-card__subtitle">{{ gateway.departmentName }}</p>
|
||||||
|
</div>
|
||||||
|
<span class="status-pill" :class="gateway.toneClass">{{ gateway.statusLabel }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-container sessions-table">
|
<div class="gateway-card__meta">
|
||||||
<table class="table is-fullwidth is-hoverable">
|
<span>
|
||||||
<thead>
|
{{ $t("system_status.gateways.labels.discovery") }}: {{ gateway.discoveryLabel }}
|
||||||
<tr>
|
|
||||||
<th>{{ $t("system_status.table.active") }}</th>
|
|
||||||
<th>{{ $t("system_status.table.name") }}</th>
|
|
||||||
<th>{{ $t("system_status.table.type") }}</th>
|
|
||||||
<th>{{ $t("system_status.table.device") }}</th>
|
|
||||||
<th>{{ $t("system_status.table.route") }}</th>
|
|
||||||
<th>{{ $t("system_status.table.first_seen") }}</th>
|
|
||||||
<th>{{ $t("system_status.table.last_seen") }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="session in sessions.recent_sessions" :key="`${session.session_kind}-${session.principal_id}-${session.last_seen_at}`">
|
|
||||||
<td>
|
|
||||||
<span class="status-pill" :class="session.active ? 'is-ok' : 'is-down'">
|
|
||||||
{{ session.active ? $t("system_status.status.active") : $t("system_status.status.inactive") }}
|
|
||||||
</span>
|
</span>
|
||||||
</td>
|
<span>
|
||||||
<td>
|
{{ $t("system_status.gateways.labels.last_heartbeat") }}: {{ gateway.lastHeartbeatLabel }}
|
||||||
<strong>{{ sessionDisplayName(session) }}</strong>
|
</span>
|
||||||
<div class="session-context">{{ sessionContextLabel(session) }}</div>
|
<span v-if="gateway.activeOperationLabel">
|
||||||
</td>
|
{{ $t("system_status.gateways.labels.active_operation") }}: {{ gateway.activeOperationLabel }}
|
||||||
<td>{{ formatSessionKind(session.session_kind) }}</td>
|
</span>
|
||||||
<td>{{ formatDeviceType(session.device_type) }}</td>
|
</div>
|
||||||
<td class="session-route">{{ session.last_route || "--" }}</td>
|
|
||||||
<td>{{ formatDate(session.first_seen_at) }}</td>
|
<p v-if="gateway.message" class="gateway-card__message">
|
||||||
<td>{{ formatDate(session.last_seen_at) }}</td>
|
{{ gateway.message }}
|
||||||
</tr>
|
</p>
|
||||||
<tr v-if="!sessions.recent_sessions?.length">
|
|
||||||
<td colspan="7">{{ $t("system_status.states.no_sessions") }}</td>
|
<RouterLink :to="gateway.link" class="module-card__link">
|
||||||
</tr>
|
{{ $t("system_status.gateways.actions.open_gateway") }}
|
||||||
</tbody>
|
</RouterLink>
|
||||||
</table>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
<div
|
||||||
|
v-else-if="!gatewayLoading && !gatewayError"
|
||||||
|
class="gateway-empty"
|
||||||
|
data-testid="gateway-empty-state"
|
||||||
|
>
|
||||||
|
{{ $t("system_status.gateways.empty") }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</b-tab-item>
|
||||||
|
|
||||||
|
<b-tab-item value="modules">
|
||||||
|
<template #header>
|
||||||
|
<b-icon pack="fas" icon="puzzle-piece" />
|
||||||
|
<span data-testid="system-status-tab-modules">
|
||||||
|
{{ $t("system_status.sections.modules") }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<section class="system-status-section" data-testid="system-status-panel-modules">
|
||||||
|
<div class="section-heading">
|
||||||
|
<h3>{{ $t("system_status.sections.modules") }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="module-grid">
|
||||||
|
<article
|
||||||
|
v-for="module in modules"
|
||||||
|
:key="module.key"
|
||||||
|
class="module-card"
|
||||||
|
:class="statusClass(module.status)"
|
||||||
|
:data-testid="`module-card-${module.key}`"
|
||||||
|
>
|
||||||
|
<div class="module-card__top">
|
||||||
|
<p class="module-card__title">{{ moduleLabel(module.key) }}</p>
|
||||||
|
<span class="status-pill" :class="statusClass(module.status)">{{ statusLabel(module.status) }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="module-card__reason" :data-testid="`module-reason-${module.key}`">
|
||||||
|
{{ moduleReasonText(module) }}
|
||||||
|
</p>
|
||||||
|
<div class="module-card__meta">
|
||||||
|
<span>{{ $t("system_status.labels.enabled") }}: {{ module.enabled ? $t("system_status.status.yes") : $t("system_status.status.no") }}</span>
|
||||||
|
<span>{{ $t("system_status.labels.configured") }}: {{ module.configured ? $t("system_status.status.yes") : $t("system_status.status.no") }}</span>
|
||||||
|
<span>{{ $t("system_status.labels.checked_at") }}: {{ formatDate(module.checked_at) }}</span>
|
||||||
|
</div>
|
||||||
|
<RouterLink v-if="modulePath(module.key)" :to="modulePath(module.key)" class="module-card__link">
|
||||||
|
{{ $t("system_status.actions.open_config") }}
|
||||||
|
</RouterLink>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</b-tab-item>
|
||||||
|
|
||||||
|
<b-tab-item value="sessions">
|
||||||
|
<template #header>
|
||||||
|
<b-icon pack="fas" icon="users" />
|
||||||
|
<span data-testid="system-status-tab-sessions">
|
||||||
|
{{ $t("system_status.sections.sessions") }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<section class="system-status-section" data-testid="system-status-panel-sessions">
|
||||||
|
<div class="section-heading">
|
||||||
|
<h3>{{ $t("system_status.sections.sessions") }}</h3>
|
||||||
|
<p>{{ $t("system_status.labels.activity_window", { minutes: sessions.active_window_minutes }) }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container sessions-table">
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ $t("system_status.table.active") }}</th>
|
||||||
|
<th>{{ $t("system_status.table.name") }}</th>
|
||||||
|
<th>{{ $t("system_status.table.type") }}</th>
|
||||||
|
<th>{{ $t("system_status.table.device") }}</th>
|
||||||
|
<th>{{ $t("system_status.table.route") }}</th>
|
||||||
|
<th>{{ $t("system_status.table.first_seen") }}</th>
|
||||||
|
<th>{{ $t("system_status.table.last_seen") }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="session in sessions.recent_sessions" :key="`${session.session_kind}-${session.principal_id}-${session.last_seen_at}`">
|
||||||
|
<td>
|
||||||
|
<span class="status-pill" :class="session.active ? 'is-ok' : 'is-down'">
|
||||||
|
{{ session.active ? $t("system_status.status.active") : $t("system_status.status.inactive") }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<strong>{{ sessionDisplayName(session) }}</strong>
|
||||||
|
<div class="session-context">{{ sessionContextLabel(session) }}</div>
|
||||||
|
</td>
|
||||||
|
<td>{{ formatSessionKind(session.session_kind) }}</td>
|
||||||
|
<td>{{ formatDeviceType(session.device_type) }}</td>
|
||||||
|
<td class="session-route">{{ session.last_route || "--" }}</td>
|
||||||
|
<td>{{ formatDate(session.first_seen_at) }}</td>
|
||||||
|
<td>{{ formatDate(session.last_seen_at) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="!sessions.recent_sessions?.length">
|
||||||
|
<td colspan="7">{{ $t("system_status.states.no_sessions") }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</b-tab-item>
|
||||||
|
</b-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -924,10 +989,10 @@ function modulePath(key) {
|
|||||||
.module-card,
|
.module-card,
|
||||||
.gateway-card {
|
.gateway-card {
|
||||||
border: 1px solid #d7dde7;
|
border: 1px solid #d7dde7;
|
||||||
border-radius: 18px;
|
border-radius: 8px;
|
||||||
padding: 1rem 1.1rem;
|
padding: 1rem 1.1rem;
|
||||||
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
background: #ffffff;
|
||||||
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.06);
|
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-card--overall {
|
.summary-card--overall {
|
||||||
@@ -991,6 +1056,23 @@ function modulePath(key) {
|
|||||||
color: #0f4c81;
|
color: #0f4c81;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.system-status-tabs {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-status-tabs :deep(.tab-content) {
|
||||||
|
padding: 1rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-status-tabs :deep(.tabs ul) {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-status-tabs :deep(.tabs a) {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
.status-card__top,
|
.status-card__top,
|
||||||
.module-card__top {
|
.module-card__top {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1107,7 +1189,7 @@ function modulePath(key) {
|
|||||||
|
|
||||||
.gateway-empty {
|
.gateway-empty {
|
||||||
border: 1px dashed #cbd5e1;
|
border: 1px dashed #cbd5e1;
|
||||||
border-radius: 18px;
|
border-radius: 8px;
|
||||||
padding: 1rem 1.1rem;
|
padding: 1rem 1.1rem;
|
||||||
color: #475569;
|
color: #475569;
|
||||||
background: rgba(248, 250, 252, 0.8);
|
background: rgba(248, 250, 252, 0.8);
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ import UserOtherVaskeabonnement
|
|||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import OrderItemsTable from "@/components/displays/department/pos/order/orderItemsTable.vue";
|
import OrderItemsTable from "@/components/displays/department/pos/order/orderItemsTable.vue";
|
||||||
import OrderContentTable from "@/components/displays/superuser/tables/OrderContentTable.vue";
|
import OrderContentTable from "@/components/displays/superuser/tables/OrderContentTable.vue";
|
||||||
|
import {
|
||||||
|
buildMultiMonthInvoiceContext,
|
||||||
|
MULTI_MONTH_INVOICE_ACTION,
|
||||||
|
promptMultiMonthInvoiceWarning,
|
||||||
|
} from "@/services/invoiceMonthSplitWarning.js";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
orders: {
|
orders: {
|
||||||
@@ -126,32 +131,34 @@ const isAnyOrderSelected = () => {
|
|||||||
return selectedInvoiceCollections.value.length > 0;
|
return selectedInvoiceCollections.value.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSelectedOrders = () => {
|
||||||
|
return props.orders.filter((order) => selectedInvoiceCollections.value.includes(order.invoice_collection_id));
|
||||||
|
}
|
||||||
|
|
||||||
/** Invoice collections */
|
/** Invoice collections */
|
||||||
const onInvoiceCollections = async () => {
|
const onInvoiceCollections = async () => {
|
||||||
// Check if any orders are selected
|
// Check if any orders are selected
|
||||||
if (selectedInvoiceCollections.value.length === 0) {
|
if (selectedInvoiceCollections.value.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/** Create the invoice */
|
const selectedOrders = getSelectedOrders();
|
||||||
const onCreateInvoiceDraft = async () => {
|
const invoiceWarningContext = buildMultiMonthInvoiceContext(selectedOrders, {
|
||||||
// Create the invoice
|
getDate: (order) => order?.created_at ?? order?.date,
|
||||||
console.log('Create invoice');
|
getInvoiceCollectionId: (order) => order?.invoice_collection_id,
|
||||||
await SessionUser.objects.collectedOrderInvoices.functions.economic.invoice(parseInt(props.collectedOrderInvoice.id)).then((response) => {
|
});
|
||||||
console.log('Invoice created successfully', response);
|
const invoiceWarningAction = await promptMultiMonthInvoiceWarning({
|
||||||
Swal.fire({
|
context: invoiceWarningContext,
|
||||||
title: 'Fakturaen er oprettet',
|
splitByMonth: SessionUser.objects.collectedOrderInvoices.functions.split_by_month,
|
||||||
text: 'Fakturaen er oprettet i E-conomic',
|
parseErrorMessage: SessionUser.functions.parseErrorMessage,
|
||||||
icon: 'success',
|
});
|
||||||
showConfirmButton: false,
|
if (invoiceWarningAction === MULTI_MONTH_INVOICE_ACTION.SPLIT) {
|
||||||
timer: 2000
|
location.reload();
|
||||||
}).then(() => {
|
return;
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log('Error creating invoice', error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
if (invoiceWarningAction === MULTI_MONTH_INVOICE_ACTION.CANCEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < selectedInvoiceCollections.value.length; i++) {
|
for (let i = 0; i < selectedInvoiceCollections.value.length; i++) {
|
||||||
const selectedInvoiceCollectionId = selectedInvoiceCollections.value[i];
|
const selectedInvoiceCollectionId = selectedInvoiceCollections.value[i];
|
||||||
// Check if the invoice collection is already booked
|
// Check if the invoice collection is already booked
|
||||||
@@ -210,6 +217,7 @@ const isOrderContentVisible = (order) => {
|
|||||||
class="button is-small"
|
class="button is-small"
|
||||||
@click="onInvoiceCollections()"
|
@click="onInvoiceCollections()"
|
||||||
:disabled="!isAnyOrderSelected()"
|
:disabled="!isAnyOrderSelected()"
|
||||||
|
data-testid="invoice-order-table-invoice-button"
|
||||||
>
|
>
|
||||||
{{ $t('global.invoice_now') }}
|
{{ $t('global.invoice_now') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -274,4 +282,4 @@ const isOrderContentVisible = (order) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const editUser = (user) => {
|
const editUser = (user) => {
|
||||||
showEditUserForm(user.id, user.customer_number, user.display_name, user.group_id);
|
showEditUserForm(user.id, user.customer_number, user.display_name, user.group_id, {
|
||||||
|
limitedBackofficeManaged: Boolean(user.limited_backoffice_managed),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { showDownloadWashCertificate } from "@/components/shop/DownloadWashCerti
|
|||||||
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
||||||
import Swal from 'sweetalert2';
|
import Swal from 'sweetalert2';
|
||||||
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
|
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
|
||||||
|
import { todayLocalDateOnly, yesterdayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
const redirectBookingObjectPage = (objectId) => {
|
const redirectBookingObjectPage = (objectId) => {
|
||||||
// Send the user to the object page
|
// Send the user to the object page
|
||||||
window.location.href = `/user/bookings/${objectId}`;
|
window.location.href = `/user/bookings/${objectId}`;
|
||||||
@@ -237,7 +238,8 @@ window.addEventListener('resize', () => {
|
|||||||
* To organize the bookings, we want to show the bookings that are created today first, then yesterday, then all other days
|
* To organize the bookings, we want to show the bookings that are created today first, then yesterday, then all other days
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
const currentDate = new Date().toISOString().split("T")[0];
|
const currentDate = todayLocalDateOnly();
|
||||||
|
const previousDate = yesterdayLocalDateOnly();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the bookings by date
|
* Sort the bookings by date
|
||||||
@@ -362,7 +364,7 @@ const canUserEditObject = (object) => {
|
|||||||
</span>
|
</span>
|
||||||
<span class="has-text-grey">
|
<span class="has-text-grey">
|
||||||
<!-- Human readable date (Today, Yesterday, etc.) -->
|
<!-- Human readable date (Today, Yesterday, etc.) -->
|
||||||
{{ object.date === currentDate ? $t('tables.bookings.today') : object.date === new Date(new Date().setDate(new Date().getDate() - 1)).toISOString().split("T")[0] ? $t('tables.bookings.yesterday') : object.date }}
|
{{ object.date === currentDate ? $t('tables.bookings.today') : object.date === previousDate ? $t('tables.bookings.yesterday') : object.date }}
|
||||||
<!-- Number of bookings on the date -->
|
<!-- Number of bookings on the date -->
|
||||||
({{ $t('tables.bookings.bookings_count', { count: objects.filter((booking) => booking.date === object.date).length }) }})
|
({{ $t('tables.bookings.bookings_count', { count: objects.filter((booking) => booking.date === object.date).length }) }})
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ const reload = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const canViewVehicle = () => SessionUser.canAccessCustomerFeature("vehicles", "list");
|
||||||
|
const canEditVehicle = () => SessionUser.canAccessCustomerFeature("vehicles", "edit");
|
||||||
|
const canDeleteVehicle = () => SessionUser.canAccessCustomerFeature("vehicles", "delete");
|
||||||
|
const canShowVehicleRowActions = () => canViewVehicle() || canDeleteVehicle();
|
||||||
|
|
||||||
const redirectUserVehiclePage = (vehicleId) => {
|
const redirectUserVehiclePage = (vehicleId) => {
|
||||||
// Send the user to the vehicle page
|
// Send the user to the vehicle page
|
||||||
window.location.href = `/user/vehicles/${vehicleId}`;
|
window.location.href = `/user/vehicles/${vehicleId}`;
|
||||||
@@ -146,6 +151,7 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
:object="object"
|
:object="object"
|
||||||
:loadList="reload"
|
:loadList="reload"
|
||||||
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
||||||
|
:permission-check-function="canEditVehicle"
|
||||||
column="reg"
|
column="reg"
|
||||||
/>
|
/>
|
||||||
<!-- Type -->
|
<!-- Type -->
|
||||||
@@ -153,6 +159,7 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
:object="object"
|
:object="object"
|
||||||
:loadList="reload"
|
:loadList="reload"
|
||||||
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
||||||
|
:permission-check-function="canEditVehicle"
|
||||||
column="type"
|
column="type"
|
||||||
:parse-function="
|
:parse-function="
|
||||||
(value) => {
|
(value) => {
|
||||||
@@ -165,6 +172,7 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
:object="object"
|
:object="object"
|
||||||
:loadList="reload"
|
:loadList="reload"
|
||||||
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
||||||
|
:permission-check-function="canEditVehicle"
|
||||||
column="wash_subscription"
|
column="wash_subscription"
|
||||||
:parse-function="
|
:parse-function="
|
||||||
(value) => {
|
(value) => {
|
||||||
@@ -174,7 +182,7 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
/>
|
/>
|
||||||
<!-- Product Options, if the wash subscription is set to true -->
|
<!-- Product Options, if the wash subscription is set to true -->
|
||||||
<td v-if="!props.compact">
|
<td v-if="!props.compact">
|
||||||
<template v-if="object.wash_subscription">
|
<template v-if="object.wash_subscription && canEditVehicle()">
|
||||||
<!-- Enabled subscription -->
|
<!-- Enabled subscription -->
|
||||||
<ActionSettingsWheelButton
|
<ActionSettingsWheelButton
|
||||||
:label="getProductOptionsLabel(object)"
|
:label="getProductOptionsLabel(object)"
|
||||||
@@ -218,13 +226,14 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
:object="object"
|
:object="object"
|
||||||
:loadList="reload"
|
:loadList="reload"
|
||||||
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
:editFunction="SessionUser.objects.vehicles.showEditObjectFieldForm"
|
||||||
|
:permission-check-function="canEditVehicle"
|
||||||
column="reference"
|
column="reference"
|
||||||
/>
|
/>
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<td>
|
<td v-if="!props.compact">
|
||||||
<!-- Actions stay grouped behind the wheel menu. -->
|
<!-- Actions stay grouped behind the wheel menu. -->
|
||||||
<ActionSettingsWheelButton
|
<ActionSettingsWheelButton
|
||||||
v-if="!props.compact"
|
v-if="canShowVehicleRowActions()"
|
||||||
:user_id="object.user_id"
|
:user_id="object.user_id"
|
||||||
:reg_1="object.reg"
|
:reg_1="object.reg"
|
||||||
:displayActionsDirectly="false"
|
:displayActionsDirectly="false"
|
||||||
@@ -232,12 +241,14 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
<template #actions>
|
<template #actions>
|
||||||
<!-- View (Redirect to the vehicle page) -->
|
<!-- View (Redirect to the vehicle page) -->
|
||||||
<ActionSettingsWheelItem
|
<ActionSettingsWheelItem
|
||||||
|
v-if="canViewVehicle()"
|
||||||
:label="$t('global.manage') + ' ' + $t('objects.vehicles.single')"
|
:label="$t('global.manage') + ' ' + $t('objects.vehicles.single')"
|
||||||
icon="fas fa-eye"
|
icon="fas fa-eye"
|
||||||
:click-action="() => redirectUserVehiclePage(object.id)"
|
:click-action="() => redirectUserVehiclePage(object.id)"
|
||||||
/>
|
/>
|
||||||
<!-- Delete -->
|
<!-- Delete -->
|
||||||
<ActionSettingsWheelItem
|
<ActionSettingsWheelItem
|
||||||
|
v-if="canDeleteVehicle()"
|
||||||
:label="$t('global.delete') + ' ' + $t('objects.vehicles.single')"
|
:label="$t('global.delete') + ' ' + $t('objects.vehicles.single')"
|
||||||
icon="fas fa-trash"
|
icon="fas fa-trash"
|
||||||
:template="'danger'"
|
:template="'danger'"
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import axios from "axios";
|
|||||||
import { API_URL } from "@/config.js";
|
import { API_URL } from "@/config.js";
|
||||||
import { getError, parseError, removeError } from "@/components/request/HandleGlobalError.vue";
|
import { getError, parseError, removeError } from "@/components/request/HandleGlobalError.vue";
|
||||||
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
|
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
|
||||||
|
import { economicEanPayloadValue, normalizeEconomicEan } from "@/services/economicCustomerIdentifiers.js";
|
||||||
|
|
||||||
const customerFormData = reactive({
|
const customerFormData = reactive({
|
||||||
cvr: null,
|
cvr: null,
|
||||||
contactEmail: null,
|
contactEmail: null,
|
||||||
contactPhone: null
|
contactPhone: null,
|
||||||
|
ean: null
|
||||||
});
|
});
|
||||||
|
|
||||||
const deliverSuccess = ref(false);
|
const deliverSuccess = ref(false);
|
||||||
@@ -17,17 +19,24 @@ let deliverTimeoutId;
|
|||||||
const submitCustomer = async () => {
|
const submitCustomer = async () => {
|
||||||
removeError("authRegisterCvr");
|
removeError("authRegisterCvr");
|
||||||
try {
|
try {
|
||||||
await axios.post(API_URL + "/auth/register/cvr", {
|
const payload = {
|
||||||
cvr: customerFormData.cvr,
|
cvr: customerFormData.cvr,
|
||||||
invoiceEmail: customerFormData.contactEmail,
|
invoiceEmail: customerFormData.contactEmail,
|
||||||
contactEmail: customerFormData.contactEmail,
|
contactEmail: customerFormData.contactEmail,
|
||||||
contactPhone: customerFormData.contactPhone,
|
contactPhone: customerFormData.contactPhone,
|
||||||
companyPhone: customerFormData.contactPhone
|
companyPhone: customerFormData.contactPhone
|
||||||
});
|
};
|
||||||
|
const ean = economicEanPayloadValue(customerFormData.ean);
|
||||||
|
if (ean !== undefined) {
|
||||||
|
payload.ean = ean;
|
||||||
|
}
|
||||||
|
|
||||||
|
await axios.post(API_URL + "/auth/register/cvr", payload);
|
||||||
|
|
||||||
customerFormData.contactPhone = null;
|
customerFormData.contactPhone = null;
|
||||||
customerFormData.contactEmail = null;
|
customerFormData.contactEmail = null;
|
||||||
customerFormData.cvr = null;
|
customerFormData.cvr = null;
|
||||||
|
customerFormData.ean = null;
|
||||||
|
|
||||||
deliverSuccess.value = true;
|
deliverSuccess.value = true;
|
||||||
deliverTimeoutId = setTimeout(() => {
|
deliverTimeoutId = setTimeout(() => {
|
||||||
@@ -59,6 +68,20 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="customer_ean">{{ $t("customer_creation.customer.ean_label") }}</label>
|
||||||
|
<input
|
||||||
|
id="customer_ean"
|
||||||
|
:value="customerFormData.ean"
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
maxlength="13"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="5790001234567"
|
||||||
|
@input="customerFormData.ean = normalizeEconomicEan($event.target.value)"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="email">{{ $t("customer_creation.customer.email_label") }}</label>
|
<label for="email">{{ $t("customer_creation.customer.email_label") }}</label>
|
||||||
<input id="email" v-model="customerFormData.contactEmail" type="email" name="email" autocomplete="email">
|
<input id="email" v-model="customerFormData.contactEmail" type="email" name="email" autocomplete="email">
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ import {
|
|||||||
getProductsCategory,
|
getProductsCategory,
|
||||||
loadOrderItems,
|
loadOrderItems,
|
||||||
productCategoryAllowed,
|
productCategoryAllowed,
|
||||||
|
isProductRestricted,
|
||||||
setProductsCategory,
|
setProductsCategory,
|
||||||
getUserProductDiscount,
|
getUserProductPrice,
|
||||||
showFakeCreateOrderItem,
|
showFakeCreateOrderItem,
|
||||||
department_id,
|
department_id,
|
||||||
user_discounts,
|
user_discounts,
|
||||||
getUserGlobalDiscount,
|
getUserGlobalDiscount,
|
||||||
getUserDiscounts,
|
getUserDiscounts,
|
||||||
customer_id,
|
customer_id,
|
||||||
customer_attributes
|
customer_attributes,
|
||||||
|
loadCustomerAttributes
|
||||||
} from "@/components/shop/POSDepartmentProcess.vue";
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import {getProductCategory, getProducts} from "@/components/shop/Products.vue";
|
import {getProductCategory, getProducts} from "@/components/shop/Products.vue";
|
||||||
import {createOrderItem} from "@/components/shop/OrdersItems.vue";
|
import {createOrderItem} from "@/components/shop/OrdersItems.vue";
|
||||||
@@ -151,6 +153,10 @@ const fetchProductsForCategory = async ({
|
|||||||
products.value = [];
|
products.value = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (customerNumber) {
|
||||||
|
await loadCustomerAttributes(customerNumber);
|
||||||
|
}
|
||||||
|
|
||||||
const response = normalizedCategory === null
|
const response = normalizedCategory === null
|
||||||
? await getProducts(departmentId, props.finalPrice, customerNumber)
|
? await getProducts(departmentId, props.finalPrice, customerNumber)
|
||||||
: await getProductCategory(normalizedCategory, departmentId, props.finalPrice, customerNumber);
|
: await getProductCategory(normalizedCategory, departmentId, props.finalPrice, customerNumber);
|
||||||
@@ -549,10 +555,9 @@ const addPreviousOrderToCurrent = async (previousOrder) => {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousOrderProduct = getPreviousOrderProduct(orderItem);
|
const previousOrderProduct = getPreviousOrderProduct(orderItem) || { id: productId, price: getPreviousOrderProductPrice(orderItem) };
|
||||||
const discount = getUserProductDiscount(previousOrderProduct || {});
|
|
||||||
const basePrice = getPreviousOrderProductPrice(orderItem);
|
const basePrice = getPreviousOrderProductPrice(orderItem);
|
||||||
const discountedPrice = (basePrice - (basePrice * (discount / 100))).toFixed(0);
|
const discountedPrice = String(getUserProductPrice({ ...previousOrderProduct, price: basePrice }));
|
||||||
|
|
||||||
showFakeCreateOrderItem(productId, quantity, discountedPrice);
|
showFakeCreateOrderItem(productId, quantity, discountedPrice);
|
||||||
await createOrderItem(orderId, productId, quantity);
|
await createOrderItem(orderId, productId, quantity);
|
||||||
@@ -586,14 +591,12 @@ const getRecommendedProductPrice = (productId) => {
|
|||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
const discount = getUserProductDiscount(product);
|
return String(getUserProductPrice(product));
|
||||||
const price = Number(product.price ?? 0);
|
|
||||||
return (price - (price * (discount / 100))).toFixed(0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isRecommendedProductCategoryAllowed = (productId) => {
|
const isRecommendedProductCategoryAllowed = (productId) => {
|
||||||
const product = getProductById(productId);
|
const product = getProductById(productId);
|
||||||
return !!product && productCategoryAllowed(product.category);
|
return !!product && !isProductRestricted(product);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRecommendedProductToOrder = async (productId) => {
|
const addRecommendedProductToOrder = async (productId) => {
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ const props = defineProps(['label', 'isText']);
|
|||||||
const label = computed(() => props.label || t('admin.pos.buttons.go_back'));
|
const label = computed(() => props.label || t('admin.pos.buttons.go_back'));
|
||||||
|
|
||||||
// Go to previous step
|
// Go to previous step
|
||||||
const goToPreviousStep = () => {
|
const goToPreviousStep = async () => {
|
||||||
// Get the department id
|
// Get the department id
|
||||||
previousStep();
|
await previousStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyIfText = (string) => props.isText ? '' : string;
|
const emptyIfText = (string) => props.isText ? '' : string;
|
||||||
@@ -32,4 +32,4 @@ const emptyIfText = (string) => props.isText ? '' : string;
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -405,6 +405,24 @@ watch(activeQuickAction, (nextMode, previousMode) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label"
|
||||||
|
><small>{{ $t("admin.pos.ean") }}</small></label
|
||||||
|
>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
v-model="addCustomerForm.ean"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
maxlength="13"
|
||||||
|
:placeholder="$t('admin.pos.ean')"
|
||||||
|
:disabled="addCustomerSearchResult === null || isAddCustomerSubmitting"
|
||||||
|
data-testid="pos-desktop-add-customer-ean"
|
||||||
|
@input="handleAddCustomerFieldInput('ean')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label"
|
<label class="label"
|
||||||
><small>{{ $t("admin.pos.invoice_email") }}</small></label
|
><small>{{ $t("admin.pos.invoice_email") }}</small></label
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, nextTick, onMounted, ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import CustomerSearchSelect from "@/components/search/economic/CustomerSearchSelect.vue";
|
||||||
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
|
|
||||||
|
const emit = defineEmits(["close", "created"]);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const selectedCustomer = ref(null);
|
||||||
|
const registrationNumber = ref("");
|
||||||
|
const vehicleType = ref("");
|
||||||
|
const washSubscription = ref(false);
|
||||||
|
const reference = ref("");
|
||||||
|
const vehicleTypeOptions = ref([]);
|
||||||
|
const isLoadingVehicleTypes = ref(false);
|
||||||
|
const isSubmitting = ref(false);
|
||||||
|
const errorMessage = ref("");
|
||||||
|
const vehicleTypeError = ref("");
|
||||||
|
|
||||||
|
const getCustomerNumber = (customer) => {
|
||||||
|
const parsedValue = Number.parseInt(
|
||||||
|
String(customer?.customerNumber ?? customer?.customer_number ?? customer?.customer_id ?? customer?.id ?? ""),
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedCustomerNumber = computed(() => getCustomerNumber(selectedCustomer.value));
|
||||||
|
|
||||||
|
const normalizedVehicleType = computed(() => {
|
||||||
|
if (vehicleType.value === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedValue = Number.parseInt(String(vehicleType.value), 10);
|
||||||
|
return Number.isInteger(parsedValue) && parsedValue >= 0 ? parsedValue : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizedRegistrationNumber = computed(() => {
|
||||||
|
return registrationNumber.value.trim().toUpperCase();
|
||||||
|
});
|
||||||
|
|
||||||
|
const canSubmit = computed(() => {
|
||||||
|
return (
|
||||||
|
!isSubmitting.value &&
|
||||||
|
!isLoadingVehicleTypes.value &&
|
||||||
|
selectedCustomerNumber.value !== null &&
|
||||||
|
normalizedRegistrationNumber.value.length > 0 &&
|
||||||
|
normalizedVehicleType.value !== null
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const parseErrorMessage = (error) => {
|
||||||
|
return SessionUser.functions.parseErrorMessage(error) || t("vehicles.add_modal.error");
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadVehicleTypes = async () => {
|
||||||
|
isLoadingVehicleTypes.value = true;
|
||||||
|
vehicleTypeError.value = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
vehicleTypeOptions.value = await SessionUser.objects.vehicles.columns.type.options();
|
||||||
|
} catch (error) {
|
||||||
|
vehicleTypeError.value = parseErrorMessage(error) || t("vehicles.add_modal.type_load_error");
|
||||||
|
} finally {
|
||||||
|
isLoadingVehicleTypes.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
if (!isSubmitting.value) {
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitVehicle = async () => {
|
||||||
|
if (!canSubmit.value) {
|
||||||
|
errorMessage.value = t("vehicles.add_modal.validation_error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmitting.value = true;
|
||||||
|
errorMessage.value = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await SessionUser.objects.vehicles.add(
|
||||||
|
normalizedVehicleType.value,
|
||||||
|
normalizedRegistrationNumber.value,
|
||||||
|
washSubscription.value,
|
||||||
|
selectedCustomerNumber.value,
|
||||||
|
reference.value.trim() || null
|
||||||
|
);
|
||||||
|
|
||||||
|
emit("created", response);
|
||||||
|
} catch (error) {
|
||||||
|
errorMessage.value = parseErrorMessage(error);
|
||||||
|
} finally {
|
||||||
|
isSubmitting.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadVehicleTypes();
|
||||||
|
await nextTick();
|
||||||
|
document.getElementById("superuser-add-vehicle-customer-search")?.focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="modal is-active" data-testid="superuser-add-vehicle-modal">
|
||||||
|
<div class="modal-background" @click="closeModal"></div>
|
||||||
|
<div class="modal-card superuser-add-vehicle-modal">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">{{ t("vehicles.add_modal.title") }}</p>
|
||||||
|
<button
|
||||||
|
class="delete"
|
||||||
|
type="button"
|
||||||
|
:aria-label="t('common.close')"
|
||||||
|
data-testid="superuser-add-vehicle-close"
|
||||||
|
@click="closeModal"
|
||||||
|
></button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<div v-if="errorMessage" class="notification is-danger is-light" data-testid="superuser-add-vehicle-error">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CustomerSearchSelect
|
||||||
|
v-model="selectedCustomer"
|
||||||
|
input-id="superuser-add-vehicle-customer-search"
|
||||||
|
test-id-prefix="superuser-add-vehicle-customer"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
:placeholder="t('vehicles.add_modal.customer_placeholder')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="superuser-add-vehicle-registration">{{ t("vehicles.form.license_plate") }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="superuser-add-vehicle-registration"
|
||||||
|
v-model="registrationNumber"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
:placeholder="t('vehicles.add_modal.registration_placeholder')"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
data-testid="superuser-add-vehicle-registration"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="superuser-add-vehicle-type">{{ t("vehicles.form.type") }}</label>
|
||||||
|
<div class="control" :class="{ 'is-loading': isLoadingVehicleTypes }">
|
||||||
|
<div class="select is-fullwidth">
|
||||||
|
<select
|
||||||
|
id="superuser-add-vehicle-type"
|
||||||
|
v-model="vehicleType"
|
||||||
|
:disabled="isSubmitting || isLoadingVehicleTypes || vehicleTypeOptions.length === 0"
|
||||||
|
data-testid="superuser-add-vehicle-type"
|
||||||
|
>
|
||||||
|
<option disabled value="">{{ t("vehicles.add_modal.type_placeholder") }}</option>
|
||||||
|
<option v-for="option in vehicleTypeOptions" :key="option.id" :value="option.id">
|
||||||
|
{{ option.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p v-if="vehicleTypeError" class="help is-danger" data-testid="superuser-add-vehicle-type-error">
|
||||||
|
{{ vehicleTypeError }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input
|
||||||
|
v-model="washSubscription"
|
||||||
|
type="checkbox"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
data-testid="superuser-add-vehicle-wash-subscription"
|
||||||
|
/>
|
||||||
|
{{ t("objects.vehicles.columns.wash_subscription") }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="superuser-add-vehicle-reference">{{ t("common.reference") }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="superuser-add-vehicle-reference"
|
||||||
|
v-model="reference"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
:placeholder="t('vehicles.add_modal.reference_placeholder')"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
data-testid="superuser-add-vehicle-reference"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot is-justify-content-flex-end">
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
type="button"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
data-testid="superuser-add-vehicle-cancel"
|
||||||
|
@click="closeModal"
|
||||||
|
>
|
||||||
|
{{ t("common.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button is-link"
|
||||||
|
type="button"
|
||||||
|
:class="{ 'is-loading': isSubmitting }"
|
||||||
|
:disabled="!canSubmit"
|
||||||
|
data-testid="superuser-add-vehicle-submit"
|
||||||
|
@click="submitVehicle"
|
||||||
|
>
|
||||||
|
{{ t("vehicles.add_modal.submit") }}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.superuser-add-vehicle-modal {
|
||||||
|
max-width: min(44rem, calc(100vw - 2rem));
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-card-body {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,7 +5,9 @@ import Swal from "sweetalert2";
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
export const showEditUserForm = async (id, customer_number, display_name, role) => {
|
export const showEditUserForm = async (id, customer_number, display_name, role, options = {}) => {
|
||||||
|
const lockedRoleFields = options.limitedBackofficeManaged ? 'disabled' : '';
|
||||||
|
|
||||||
await Swal.fire({
|
await Swal.fire({
|
||||||
title: 'Edit User',
|
title: 'Edit User',
|
||||||
html: `
|
html: `
|
||||||
@@ -13,7 +15,7 @@ export const showEditUserForm = async (id, customer_number, display_name, role)
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Customer Number</label>
|
<label class="label">Customer Number</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" id="customer_number" value="${customer_number}" />
|
<input class="input" type="text" id="customer_number" value="${customer_number}" ${lockedRoleFields} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -25,7 +27,7 @@ export const showEditUserForm = async (id, customer_number, display_name, role)
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Role</label>
|
<label class="label">Role</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" id="role" value="${role}" />
|
<input class="input" type="text" id="role" value="${role}" ${lockedRoleFields} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -50,4 +52,4 @@ export const showEditUserForm = async (id, customer_number, display_name, role)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -19,17 +19,17 @@ const SENSITIVE_QUERY_KEYS = /authorization|password|passwd|secret|token|api[_-]
|
|||||||
const FALLBACK_LABELS = {
|
const FALLBACK_LABELS = {
|
||||||
"error_report.button": "Report error",
|
"error_report.button": "Report error",
|
||||||
"error_report.title": "Report error",
|
"error_report.title": "Report error",
|
||||||
"error_report.subtitle": "Send the current screen and recent error details to support.",
|
"error_report.subtitle": "Send recent error details to support. A screenshot is attached when available.",
|
||||||
"error_report.before_error": "What were you doing before the error occurred?",
|
"error_report.before_error": "What were you doing before the error occurred?",
|
||||||
"error_report.expected": "What did you expect would happen?",
|
"error_report.expected": "What did you expect would happen?",
|
||||||
"error_report.actual": "What actually happened?",
|
"error_report.actual": "What actually happened?",
|
||||||
"error_report.before_error_placeholder": "Describe the action you were taking, for example opening orders or selecting a customer.",
|
"error_report.before_error_placeholder": "Describe the action you were taking, for example opening orders or selecting a customer.",
|
||||||
"error_report.expected_placeholder": "Describe the result you expected to see.",
|
"error_report.expected_placeholder": "Describe the result you expected to see.",
|
||||||
"error_report.actual_placeholder": "Describe what you saw instead, including any error text.",
|
"error_report.actual_placeholder": "Describe what you saw instead, including any error text.",
|
||||||
"error_report.consent": "I accept that the current app screen, recent request errors, Vue errors, browser details, and my answers are collected for troubleshooting.",
|
"error_report.consent": "I accept that recent request errors, Vue errors, browser details, my answers, and an app screenshot when available are collected for troubleshooting.",
|
||||||
"error_report.submit": "Submit report",
|
"error_report.submit": "Submit report",
|
||||||
"error_report.submitted": "Error report submitted.",
|
"error_report.submitted": "Error report submitted.",
|
||||||
"error_report.capture_failed": "The screen capture failed. Please try again.",
|
"error_report.capture_failed": "The screen capture failed. The report will be sent without a screenshot.",
|
||||||
"error_report.submit_failed": "The error report could not be submitted.",
|
"error_report.submit_failed": "The error report could not be submitted.",
|
||||||
"error_report.required": "All fields and data collection acceptance are required.",
|
"error_report.required": "All fields and data collection acceptance are required.",
|
||||||
};
|
};
|
||||||
@@ -277,15 +277,18 @@ const submit = async () => {
|
|||||||
|
|
||||||
isSubmitting.value = true;
|
isSubmitting.value = true;
|
||||||
try {
|
try {
|
||||||
let screenshot = "";
|
let screenshot = null;
|
||||||
try {
|
try {
|
||||||
screenshot = await captureScreenshot();
|
screenshot = await captureScreenshot();
|
||||||
} catch {
|
} catch {
|
||||||
captureError.value = tr("error_report.capture_failed");
|
captureError.value = tr("error_report.capture_failed");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = buildContext();
|
const context = buildContext();
|
||||||
|
context.screenshot_attachment = {
|
||||||
|
status: screenshot ? "stored" : "capture_failed",
|
||||||
|
attached: Boolean(screenshot),
|
||||||
|
};
|
||||||
await submitErrorReport({
|
await submitErrorReport({
|
||||||
before_error: form.before_error.trim(),
|
before_error: form.before_error.trim(),
|
||||||
expected: form.expected.trim(),
|
expected: form.expected.trim(),
|
||||||
@@ -340,7 +343,7 @@ const submit = async () => {
|
|||||||
<div v-if="submitted" class="notification is-success is-light" data-testid="error-report-submitted">
|
<div v-if="submitted" class="notification is-success is-light" data-testid="error-report-submitted">
|
||||||
{{ tr("error_report.submitted") }}
|
{{ tr("error_report.submitted") }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="captureError" class="notification is-danger is-light">
|
<div v-if="captureError" class="notification is-warning is-light" data-testid="error-report-capture-warning">
|
||||||
{{ captureError }}
|
{{ captureError }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="submitError" class="notification is-danger is-light">
|
<div v-if="submitError" class="notification is-danger is-light">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ref, watch, onMounted } from "vue";
|
|||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { isAccessibleVisibleNamedDepartment, sortByDepartmentPriorityOrder } from "@/services/departmentVisibility.js";
|
import { isAccessibleVisibleNamedDepartment, sortByDepartmentPriorityOrder } from "@/services/departmentVisibility.js";
|
||||||
|
import { todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
// Get the department ID from the URL
|
// Get the department ID from the URL
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const department_id = route.params.departmentId || SessionUser.functions.getDepartmentIdFromUrl() || null;
|
const department_id = route.params.departmentId || SessionUser.functions.getDepartmentIdFromUrl() || null;
|
||||||
@@ -73,9 +74,9 @@ const emphasis = ref({
|
|||||||
"department:" +
|
"department:" +
|
||||||
department_id +
|
department_id +
|
||||||
",status:pending,date-date_from:" +
|
",status:pending,date-date_from:" +
|
||||||
new Date().toISOString().split("T")[0] +
|
todayLocalDateOnly() +
|
||||||
",date-date_to:" +
|
",date-date_to:" +
|
||||||
new Date().toISOString().split("T")[0],
|
todayLocalDateOnly(),
|
||||||
limit: 1, // Limit to 1 booking, we only need to know if there are any bookings or not
|
limit: 1, // Limit to 1 booking, we only need to know if there are any bookings or not
|
||||||
page: 1,
|
page: 1,
|
||||||
})
|
})
|
||||||
@@ -97,7 +98,7 @@ const emphasis = ref({
|
|||||||
* Check if the department has a daily report for today
|
* Check if the department has a daily report for today
|
||||||
*/
|
*/
|
||||||
SessionUser.request(SessionUser.objects.department_daily_reports.meta.endpoint + "/get", "GET", {
|
SessionUser.request(SessionUser.objects.department_daily_reports.meta.endpoint + "/get", "GET", {
|
||||||
date: new Date().toISOString().split("T")[0],
|
date: todayLocalDateOnly(),
|
||||||
id: department_id, // This refers to the department ID
|
id: department_id, // This refers to the department ID
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|||||||
@@ -75,19 +75,11 @@ const items = computed<NavigationItemProps[]>(() => [
|
|||||||
* ]
|
* ]
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
{
|
|
||||||
label: t('templates.limited_backoffice.title'),
|
|
||||||
to: `/backoffice`,
|
|
||||||
type: 'link',
|
|
||||||
permissions: ['limited_backoffice_access'],
|
|
||||||
hidden: hasPermission('superuser'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t('common.settings'),
|
label: t('common.settings'),
|
||||||
to: `/user/profile`,
|
to: `/user/profile`,
|
||||||
type: 'link',
|
type: 'link',
|
||||||
permissions: ['user'],
|
hidden: !window.location.pathname.startsWith('/user') || !SessionUser.authenticated.value
|
||||||
hidden: !window.location.pathname.startsWith('/user')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('common.logout'),
|
label: t('common.logout'),
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const items = computed<NavigationItemProps[]>(() => [
|
|||||||
to: `/user`,
|
to: `/user`,
|
||||||
type: 'link',
|
type: 'link',
|
||||||
// Dashboard visible to regular users and all subusers (subusers always have at least one grant)
|
// Dashboard visible to regular users and all subusers (subusers always have at least one grant)
|
||||||
permissions: ['user', 'BOOKINGS_LIST', 'ORDERS_LIST', 'VEHICLES_LIST', 'SELFSERVE_LIST', 'SUBUSERS_LIST'],
|
permissions: ['user', 'BOOKINGS_LIST', 'BOOKINGS_ADD', 'ORDERS_LIST', 'VEHICLES_LIST', 'VEHICLES_ADD', 'SELFSERVE_LIST', 'SUBUSERS_LIST'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('common.vehicles'),
|
label: t('common.vehicles'),
|
||||||
|
|||||||
@@ -0,0 +1,288 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { isSearching, searchCustomer, searchCustomerResults } from "@/components/search/economic/customerSearch.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
inputId: {
|
||||||
|
type: String,
|
||||||
|
default: "customer-search-select-input",
|
||||||
|
},
|
||||||
|
testIdPrefix: {
|
||||||
|
type: String,
|
||||||
|
default: "customer-search-select",
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:modelValue", "selected", "cleared"]);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const searchQuery = ref("");
|
||||||
|
const showResults = ref(false);
|
||||||
|
const selectedResultIndex = ref(-1);
|
||||||
|
|
||||||
|
const getCustomerNumber = (customer) => {
|
||||||
|
const parsedValue = Number.parseInt(
|
||||||
|
String(customer?.customerNumber ?? customer?.customer_number ?? customer?.customer_id ?? customer?.id ?? ""),
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCustomerName = (customer) => {
|
||||||
|
return String(customer?.name ?? customer?.customer_name ?? customer?.customerName ?? "").trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCustomerCity = (customer) => {
|
||||||
|
return String(customer?.city ?? customer?.address_city ?? "").trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCustomer = (customer) => {
|
||||||
|
if (!customer) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = getCustomerName(customer);
|
||||||
|
const customerNumber = getCustomerNumber(customer);
|
||||||
|
|
||||||
|
return [name, customerNumber ? `#${customerNumber}` : null].filter(Boolean).join(" - ");
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedCustomerNumber = computed(() => getCustomerNumber(props.modelValue));
|
||||||
|
const selectedCustomerName = computed(() => getCustomerName(props.modelValue));
|
||||||
|
const selectedCustomerCity = computed(() => getCustomerCity(props.modelValue));
|
||||||
|
const hasResults = computed(() => searchCustomerResults.value.length > 0);
|
||||||
|
const placeholderText = computed(() => props.placeholder || t("vehicles.add_modal.customer_placeholder"));
|
||||||
|
|
||||||
|
const resetSearchResults = () => {
|
||||||
|
searchCustomer(null);
|
||||||
|
selectedResultIndex.value = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setSelectedCustomer = (customer) => {
|
||||||
|
emit("update:modelValue", customer);
|
||||||
|
emit("selected", customer);
|
||||||
|
searchQuery.value = formatCustomer(customer);
|
||||||
|
showResults.value = false;
|
||||||
|
selectedResultIndex.value = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSelectedCustomer = async () => {
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
emit("cleared");
|
||||||
|
searchQuery.value = "";
|
||||||
|
showResults.value = false;
|
||||||
|
resetSearchResults();
|
||||||
|
await nextTick();
|
||||||
|
document.getElementById(props.inputId)?.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearchInput = () => {
|
||||||
|
if (props.modelValue) {
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = searchQuery.value.trim();
|
||||||
|
selectedResultIndex.value = -1;
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
showResults.value = false;
|
||||||
|
resetSearchResults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showResults.value = true;
|
||||||
|
searchCustomer(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (searchQuery.value.trim() && hasResults.value) {
|
||||||
|
showResults.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
showResults.value = false;
|
||||||
|
selectedResultIndex.value = -1;
|
||||||
|
}, 150);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeydown = (event) => {
|
||||||
|
if (!showResults.value || !hasResults.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowDown") {
|
||||||
|
event.preventDefault();
|
||||||
|
selectedResultIndex.value = Math.min(selectedResultIndex.value + 1, searchCustomerResults.value.length - 1);
|
||||||
|
} else if (event.key === "ArrowUp") {
|
||||||
|
event.preventDefault();
|
||||||
|
selectedResultIndex.value = Math.max(selectedResultIndex.value - 1, 0);
|
||||||
|
} else if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
const selectedCustomer = searchCustomerResults.value[selectedResultIndex.value] || searchCustomerResults.value[0];
|
||||||
|
if (selectedCustomer) {
|
||||||
|
setSelectedCustomer(selectedCustomer);
|
||||||
|
}
|
||||||
|
} else if (event.key === "Escape") {
|
||||||
|
showResults.value = false;
|
||||||
|
selectedResultIndex.value = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(customer) => {
|
||||||
|
if (customer) {
|
||||||
|
searchQuery.value = formatCustomer(customer);
|
||||||
|
} else if (!document.activeElement || document.activeElement.id !== props.inputId) {
|
||||||
|
searchQuery.value = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
resetSearchResults();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="customer-search-select">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" :for="inputId">{{ t("vehicles.add_modal.customer_label") }}</label>
|
||||||
|
<div class="control has-icons-left" :class="{ 'is-loading': isSearching }">
|
||||||
|
<input
|
||||||
|
:id="inputId"
|
||||||
|
v-model="searchQuery"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
:placeholder="placeholderText"
|
||||||
|
:disabled="disabled"
|
||||||
|
:aria-expanded="showResults && hasResults"
|
||||||
|
:data-testid="`${testIdPrefix}-input`"
|
||||||
|
@input="handleSearchInput"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
|
@keydown="handleKeydown"
|
||||||
|
/>
|
||||||
|
<span class="icon is-left">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="showResults && hasResults"
|
||||||
|
class="dropdown is-active customer-search-select__dropdown"
|
||||||
|
:data-testid="`${testIdPrefix}-results`"
|
||||||
|
>
|
||||||
|
<div class="dropdown-menu customer-search-select__menu" role="listbox">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<button
|
||||||
|
v-for="(customer, index) in searchCustomerResults"
|
||||||
|
:key="getCustomerNumber(customer) || index"
|
||||||
|
type="button"
|
||||||
|
class="dropdown-item customer-search-select__option"
|
||||||
|
:class="{ 'is-active': selectedResultIndex === index }"
|
||||||
|
:data-testid="`${testIdPrefix}-option-${index}`"
|
||||||
|
@mousedown.prevent="setSelectedCustomer(customer)"
|
||||||
|
>
|
||||||
|
<span class="customer-search-select__option-main">{{ getCustomerName(customer) }}</span>
|
||||||
|
<span class="customer-search-select__option-meta">
|
||||||
|
#{{ getCustomerNumber(customer) }}
|
||||||
|
<template v-if="getCustomerCity(customer)"> · {{ getCustomerCity(customer) }}</template>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="modelValue" class="customer-search-select__selected" :data-testid="`${testIdPrefix}-selected`">
|
||||||
|
<div>
|
||||||
|
<p class="has-text-weight-semibold">{{ t("vehicles.add_modal.selected_customer") }}</p>
|
||||||
|
<p>{{ selectedCustomerName }}</p>
|
||||||
|
<p class="is-size-7 has-text-grey">
|
||||||
|
#{{ selectedCustomerNumber }}
|
||||||
|
<span v-if="selectedCustomerCity"> · {{ selectedCustomerCity }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button is-small is-light"
|
||||||
|
:disabled="disabled"
|
||||||
|
:data-testid="`${testIdPrefix}-clear`"
|
||||||
|
@click="clearSelectedCustomer"
|
||||||
|
>
|
||||||
|
{{ t("common.clear") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.customer-search-select {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-search-select__dropdown,
|
||||||
|
.customer-search-select__menu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-search-select__dropdown {
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 4.75rem;
|
||||||
|
z-index: 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-search-select__option {
|
||||||
|
align-items: flex-start;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: left;
|
||||||
|
white-space: normal;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-search-select__option-main {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-search-select__option-meta {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-search-select__selected {
|
||||||
|
align-items: flex-start;
|
||||||
|
background: #f5f8fc;
|
||||||
|
border: 1px solid #d8e2ef;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 0.85rem 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -89,6 +89,66 @@ const SUBUSER_MANAGEMENT_PERMISSIONS = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CUSTOMER_FEATURE_PERMISSIONS = {
|
||||||
|
vehicles: {
|
||||||
|
list: {
|
||||||
|
customer: ["user", "list_own_vehicles"],
|
||||||
|
subuser: ["VEHICLES_LIST"],
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
customer: ["user", "add_vehicle"],
|
||||||
|
subuser: ["VEHICLES_ADD"],
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
customer: ["edit_vehicle", "add_vehicle"],
|
||||||
|
subuser: ["VEHICLES_EDIT"],
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
customer: ["delete_vehicle"],
|
||||||
|
subuser: ["VEHICLES_DELETE"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bookings: {
|
||||||
|
list: {
|
||||||
|
customer: ["user", "list_own_bookings"],
|
||||||
|
subuser: ["BOOKINGS_LIST"],
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
customer: ["user"],
|
||||||
|
subuser: ["BOOKINGS_ADD"],
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
customer: ["edit_own_bookings"],
|
||||||
|
subuser: ["BOOKINGS_EDIT"],
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
customer: ["delete_own_bookings"],
|
||||||
|
subuser: ["BOOKINGS_DELETE"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orders: {
|
||||||
|
list: {
|
||||||
|
customer: ["user", "list_own_orders"],
|
||||||
|
subuser: ["ORDERS_LIST"],
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
customer: ["edit_own_orders"],
|
||||||
|
subuser: ["ORDERS_EDIT"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selfserve: {
|
||||||
|
list: {
|
||||||
|
customer: ["user", "list_own_department_selfserve_vehicle_conditions"],
|
||||||
|
subuser: ["SELFSERVE_LIST"],
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
customer: ["add_own_department_selfserve_vehicle_conditions"],
|
||||||
|
subuser: ["SELFSERVE_ADD"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
subusers: SUBUSER_MANAGEMENT_PERMISSIONS,
|
||||||
|
};
|
||||||
|
|
||||||
const getStoredSessionSnapshot = () => {
|
const getStoredSessionSnapshot = () => {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return null;
|
return null;
|
||||||
@@ -858,6 +918,13 @@ export const SessionUser = {
|
|||||||
}
|
}
|
||||||
return SessionUser.permissions.value.includes(permission) || SessionUser.permissions.value.includes("superuser");
|
return SessionUser.permissions.value.includes(permission) || SessionUser.permissions.value.includes("superuser");
|
||||||
},
|
},
|
||||||
|
hasAnyPermission: (permissions = []) => {
|
||||||
|
const permissionList = Array.isArray(permissions) ? permissions : [permissions].filter(Boolean);
|
||||||
|
if (permissionList.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return permissionList.some((permission) => SessionUser.hasPermission(permission));
|
||||||
|
},
|
||||||
/** Can access shortcuts */
|
/** Can access shortcuts */
|
||||||
canAccessAdmin: () => {
|
canAccessAdmin: () => {
|
||||||
return SessionUser.hasPermission("admin") || SessionUser.hasPermission("superuser");
|
return SessionUser.hasPermission("admin") || SessionUser.hasPermission("superuser");
|
||||||
@@ -877,6 +944,15 @@ export const SessionUser = {
|
|||||||
? SessionUser.hasPermission(permissions.subuser)
|
? SessionUser.hasPermission(permissions.subuser)
|
||||||
: SessionUser.hasPermission(permissions.customer);
|
: SessionUser.hasPermission(permissions.customer);
|
||||||
},
|
},
|
||||||
|
canAccessCustomerFeature: (feature, action = "list") => {
|
||||||
|
const permissions = CUSTOMER_FEATURE_PERMISSIONS[feature]?.[action];
|
||||||
|
if (!permissions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionList = SessionUser.isSubuser.value ? permissions.subuser : permissions.customer;
|
||||||
|
return SessionUser.hasAnyPermission(permissionList);
|
||||||
|
},
|
||||||
canAccessDepartment: (id = 0) => {
|
canAccessDepartment: (id = 0) => {
|
||||||
return SessionUser.hasPermission("department_access_" + parseInt(id)) || SessionUser.canAccessSuperUser();
|
return SessionUser.hasPermission("department_access_" + parseInt(id)) || SessionUser.canAccessSuperUser();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/Ob
|
|||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
import { todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
const t = (key) => i18n.global.t(key);
|
const t = (key) => i18n.global.t(key);
|
||||||
|
|
||||||
@@ -241,7 +242,7 @@ export const Bookings = {
|
|||||||
'/bookings',
|
'/bookings',
|
||||||
'GET',
|
'GET',
|
||||||
{
|
{
|
||||||
filters: `department:${department},status:pending,date-date_from:${new Date().toISOString().split('T')[0]},date-date_to:${new Date().toISOString().split('T')[0]}`,
|
filters: `department:${department},status:pending,date-date_from:${todayLocalDateOnly()},date-date_to:${todayLocalDateOnly()}`,
|
||||||
limit: 100,
|
limit: 100,
|
||||||
page: 1
|
page: 1
|
||||||
}
|
}
|
||||||
@@ -289,4 +290,4 @@ export const Bookings = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -444,10 +444,14 @@ export const CollectedOrderInvoices = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
split_by_month: async (dateFrom, dateTo, options = {}) => {
|
split_by_month: async (dateFrom, dateTo, options = {}) => {
|
||||||
|
const invoiceCollectionIds = Array.isArray(options.invoiceCollectionIds)
|
||||||
|
? options.invoiceCollectionIds
|
||||||
|
: options.invoice_collection_ids;
|
||||||
return authenticatedRequest('/collected-invoices/split-by-month', 'POST', {
|
return authenticatedRequest('/collected-invoices/split-by-month', 'POST', {
|
||||||
dateFrom,
|
dateFrom,
|
||||||
dateTo,
|
dateTo,
|
||||||
...(options.preview !== undefined ? { preview: !!options.preview } : {}),
|
...(options.preview !== undefined ? { preview: !!options.preview } : {}),
|
||||||
|
...(Array.isArray(invoiceCollectionIds) ? { invoice_collection_ids: invoiceCollectionIds } : {}),
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/Ob
|
|||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
import { todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
const t = (key) => i18n.global.t(key);
|
const t = (key) => i18n.global.t(key);
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ const t = (key) => i18n.global.t(key);
|
|||||||
water_usage_morning: parseInt(water_usage_morning),
|
water_usage_morning: parseInt(water_usage_morning),
|
||||||
water_usage: parseInt(water_usage),
|
water_usage: parseInt(water_usage),
|
||||||
notes: notes ? notes : 'Daily report',
|
notes: notes ? notes : 'Daily report',
|
||||||
date: date ? date : new Date().toISOString().split('T')[0]
|
date: date ? date : todayLocalDateOnly()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/ObjectsGlobal.vue";
|
import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/ObjectsGlobal.vue";
|
||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
|
import { formatLocalDateOnly, todayLocalDateOnly } from "@/services/dateOnly.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DepartmentTimeBookingsEntries object
|
* The DepartmentTimeBookingsEntries object
|
||||||
@@ -230,10 +231,10 @@ export const DepartmentTimeBookingsEntries = {
|
|||||||
if (!dateFrom) {
|
if (!dateFrom) {
|
||||||
const lastWeek = new Date();
|
const lastWeek = new Date();
|
||||||
lastWeek.setDate(lastWeek.getDate() - 7);
|
lastWeek.setDate(lastWeek.getDate() - 7);
|
||||||
dateFrom = lastWeek.toISOString().split('T')[0]; // Format as YYYY-MM-DD
|
dateFrom = formatLocalDateOnly(lastWeek); // Format as YYYY-MM-DD
|
||||||
}
|
}
|
||||||
if (!dateTo) {
|
if (!dateTo) {
|
||||||
dateTo = new Date().toISOString().split('T')[0]; // Format as YYYY-MM-DD
|
dateTo = todayLocalDateOnly(); // Format as YYYY-MM-DD
|
||||||
}
|
}
|
||||||
console.log("Fetching public available times for department:", departmentId, "from", dateFrom, "to", dateTo);
|
console.log("Fetching public available times for department:", departmentId, "from", dateFrom, "to", dateTo);
|
||||||
// Make the request to fetch public available times for the department
|
// Make the request to fetch public available times for the department
|
||||||
@@ -279,4 +280,4 @@ export const DepartmentTimeBookingsEntries = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -108,6 +108,14 @@ export const Departments = {
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
custom_pricing_only: {
|
||||||
|
label: t("objects.departments.columns.custom_pricing_only"),
|
||||||
|
type: "boolean",
|
||||||
|
sortable: true,
|
||||||
|
creation: {
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
longitude: {
|
longitude: {
|
||||||
label: t("objects.departments.columns.longitude"),
|
label: t("objects.departments.columns.longitude"),
|
||||||
type: "number",
|
type: "number",
|
||||||
@@ -164,6 +172,14 @@ export const Departments = {
|
|||||||
archived: async (id, archived) => {
|
archived: async (id, archived) => {
|
||||||
return ObjectsGlobal.set.column(Departments.meta.endpoint, id, "archived", ObjectsGlobal.parse.boolean(archived));
|
return ObjectsGlobal.set.column(Departments.meta.endpoint, id, "archived", ObjectsGlobal.parse.boolean(archived));
|
||||||
},
|
},
|
||||||
|
custom_pricing_only: async (id, custom_pricing_only) => {
|
||||||
|
return ObjectsGlobal.set.column(
|
||||||
|
Departments.meta.endpoint,
|
||||||
|
id,
|
||||||
|
"custom_pricing_only",
|
||||||
|
ObjectsGlobal.parse.boolean(custom_pricing_only)
|
||||||
|
);
|
||||||
|
},
|
||||||
longitude: async (id, longitude) => {
|
longitude: async (id, longitude) => {
|
||||||
return ObjectsGlobal.set.column(Departments.meta.endpoint, id, "longitude", parseFloat(longitude));
|
return ObjectsGlobal.set.column(Departments.meta.endpoint, id, "longitude", parseFloat(longitude));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import { getAttributes } from "@/components/shop/CustomerAttributes.vue";
|
|||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
||||||
import { doesOrderContainWashCertificateProduct } from "@/components/displays/department/pos/utils/washCertificate.js";
|
import { doesOrderContainWashCertificateProduct } from "@/components/displays/department/pos/utils/washCertificate.js";
|
||||||
|
import {
|
||||||
|
isProductCategoryRestrictedForCustomer,
|
||||||
|
isProductRestrictedForCustomer,
|
||||||
|
} from "@/features/customer/customerProductRules.js";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,6 +56,42 @@ export const nextStepDelay = ref(0); // 2 seconds delay
|
|||||||
export const isCreatingOrder = ref(false);
|
export const isCreatingOrder = ref(false);
|
||||||
export const desktopStep1PreflightHandler = ref(null);
|
export const desktopStep1PreflightHandler = ref(null);
|
||||||
let createOrderRequest = null;
|
let createOrderRequest = null;
|
||||||
|
const posStepSaveBarriers = new Set();
|
||||||
|
let latestOrderDetailsRequestId = 0;
|
||||||
|
let latestOrderMetadataMutationSequence = 0;
|
||||||
|
let activeOrderMetadataMutationCount = 0;
|
||||||
|
|
||||||
|
export const registerPosStepSaveBarrier = (handler) => {
|
||||||
|
if (typeof handler !== "function") {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
posStepSaveBarriers.add(handler);
|
||||||
|
return () => {
|
||||||
|
posStepSaveBarriers.delete(handler);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const unregisterPosStepSaveBarrier = (handler) => {
|
||||||
|
posStepSaveBarriers.delete(handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const flushPosStepSaveBarriers = async () => {
|
||||||
|
const barriers = Array.from(posStepSaveBarriers);
|
||||||
|
if (barriers.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(barriers.map((handler) => handler()));
|
||||||
|
const failedResult = results.find((result) => result.status === "rejected");
|
||||||
|
|
||||||
|
if (failedResult) {
|
||||||
|
parseError(failedResult.reason, "stepError");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
export const setDesktopStep1PreflightHandler = (handler = null) => {
|
export const setDesktopStep1PreflightHandler = (handler = null) => {
|
||||||
desktopStep1PreflightHandler.value = typeof handler === "function" ? handler : null;
|
desktopStep1PreflightHandler.value = typeof handler === "function" ? handler : null;
|
||||||
@@ -109,10 +149,16 @@ export const nextStep = async (options = { isMobile: false, orderCreation: true
|
|||||||
}
|
}
|
||||||
// Set the delay to 2 seconds
|
// Set the delay to 2 seconds
|
||||||
setNextStepDelay(2);
|
setNextStepDelay(2);
|
||||||
|
if (!(await flushPosStepSaveBarriers())) {
|
||||||
|
nextStepDelay.value = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Clear the errors
|
// Clear the errors
|
||||||
clearErrors();
|
clearErrors();
|
||||||
// If the current step is 1, create the order
|
// If the current step is 1, create the order
|
||||||
if (step.value === 1) {
|
if (step.value === 1) {
|
||||||
|
await loadCustomerAttributes(customer_id.value);
|
||||||
|
|
||||||
if (normalizedOptions.orderCreation === false) {
|
if (normalizedOptions.orderCreation === false) {
|
||||||
step.value++;
|
step.value++;
|
||||||
return;
|
return;
|
||||||
@@ -188,8 +234,22 @@ export const nextStep = async (options = { isMobile: false, orderCreation: true
|
|||||||
step.value++;
|
step.value++;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const previousStep = () => {
|
export const previousStep = async () => {
|
||||||
step.value--;
|
if (nextStepDelay.value > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await flushPosStepSaveBarriers())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
step.value = Math.max(1, step.value - 1);
|
||||||
|
|
||||||
|
if (order_id.value) {
|
||||||
|
pushPosRouteState(`id=${order_id.value}&customer_id=${customer_id.value}&step=${step.value}`);
|
||||||
|
} else {
|
||||||
|
pushPosRouteState(`step=${step.value}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCurrentStep = () => {
|
export const getCurrentStep = () => {
|
||||||
@@ -784,6 +844,129 @@ watch([reg_1, reg_2, reg_3], () => {
|
|||||||
reg_3.value = normalizeRegistrationValue(reg_3.value);
|
reg_3.value = normalizeRegistrationValue(reg_3.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const applyOrderMetadataFieldToPosState = (field, value) => {
|
||||||
|
if (field === "reference") {
|
||||||
|
reference.value = normalizeOrderMetadataString(value);
|
||||||
|
return reference.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === "notes") {
|
||||||
|
order_notes.value = normalizeOrderMetadataString(value);
|
||||||
|
return order_notes.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === "po") {
|
||||||
|
order_po.value = normalizeOrderMetadataString(value);
|
||||||
|
return order_po.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === "safety_seal") {
|
||||||
|
order_safety_seal.value = normalizeOrderMetadataString(value);
|
||||||
|
return order_safety_seal.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === "reg_1") {
|
||||||
|
reg_1.value = normalizeRegistrationValue(value);
|
||||||
|
return reg_1.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === "reg_2") {
|
||||||
|
reg_2.value = normalizeRegistrationValue(value);
|
||||||
|
return reg_2.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field === "reg_3") {
|
||||||
|
reg_3.value = normalizeRegistrationValue(value);
|
||||||
|
return reg_3.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeOrderMetadataString(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const applyOrderDetailsToPosState = (orderData, includes = {}) => {
|
||||||
|
if (!orderData || typeof orderData !== "object") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const includedCustomer = includes?.customer ?? null;
|
||||||
|
const normalizedOrderCustomerNumber = resolveCustomerNumber(orderData?.customer_id);
|
||||||
|
const selectedCustomerBeforeHydration = getSelectedCustomerNumber();
|
||||||
|
|
||||||
|
department_id.value = orderData.department_id;
|
||||||
|
applyOrderMetadataFieldToPosState("reference", orderData.reference);
|
||||||
|
applyOrderMetadataFieldToPosState("notes", orderData.notes);
|
||||||
|
applyOrderMetadataFieldToPosState("po", orderData.po);
|
||||||
|
applyOrderMetadataFieldToPosState("safety_seal", orderData.safety_seal);
|
||||||
|
applyOrderMetadataFieldToPosState("reg_1", orderData.reg_1);
|
||||||
|
applyOrderMetadataFieldToPosState("reg_2", orderData.reg_2);
|
||||||
|
applyOrderMetadataFieldToPosState("reg_3", orderData.reg_3);
|
||||||
|
invoiceCollectionId.value = orderData.invoice_collection_id || null;
|
||||||
|
completed_at.value = orderData.completed_at || null;
|
||||||
|
|
||||||
|
if (includedCustomer && typeof includedCustomer === "object") {
|
||||||
|
const customerSource =
|
||||||
|
includedCustomer.economic_customer && typeof includedCustomer.economic_customer === "object"
|
||||||
|
? includedCustomer.economic_customer
|
||||||
|
: includedCustomer;
|
||||||
|
|
||||||
|
selectCustomer(
|
||||||
|
normalizeCustomerRecord(customerSource, {
|
||||||
|
...includedCustomer,
|
||||||
|
customerNumber: normalizedOrderCustomerNumber ?? resolveCustomerNumber(includedCustomer),
|
||||||
|
name: includedCustomer.name ?? includedCustomer.customer_name ?? orderData.customer_name ?? customer_name.value,
|
||||||
|
customer_name:
|
||||||
|
includedCustomer.customer_name ?? includedCustomer.name ?? orderData.customer_name ?? customer_name.value,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (normalizedOrderCustomerNumber) {
|
||||||
|
customer_id.value = normalizedOrderCustomerNumber;
|
||||||
|
if (
|
||||||
|
selectedCustomerBeforeHydration !== normalizedOrderCustomerNumber ||
|
||||||
|
customer_name.value === "" ||
|
||||||
|
!customer_data.value
|
||||||
|
) {
|
||||||
|
void searchAndSelectCustomer(normalizedOrderCustomerNumber, { forceRefresh: true });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearSelectedCustomerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveOrderMetadataField = async (field, value, targetOrderId = order_id.value) => {
|
||||||
|
const normalizedOrderId = toPositiveInteger(targetOrderId);
|
||||||
|
const setter = SessionUser.objects.orders.set?.[field];
|
||||||
|
|
||||||
|
if (!normalizedOrderId || typeof setter !== "function") {
|
||||||
|
throw new Error(`Unable to save order field: ${field}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutationSequence = latestOrderMetadataMutationSequence + 1;
|
||||||
|
latestOrderMetadataMutationSequence = mutationSequence;
|
||||||
|
activeOrderMetadataMutationCount += 1;
|
||||||
|
const isRegistrationField = field === "reg_1" || field === "reg_2" || field === "reg_3";
|
||||||
|
const requestValue = isRegistrationField
|
||||||
|
? String(value ?? "").toUpperCase()
|
||||||
|
: normalizeOrderMetadataString(value);
|
||||||
|
const normalizedValue = isRegistrationField ? normalizeRegistrationValue(value) : requestValue;
|
||||||
|
try {
|
||||||
|
const response = await setter(normalizedOrderId, requestValue);
|
||||||
|
const updatedOrder = response?.data?.data;
|
||||||
|
|
||||||
|
if (updatedOrder && toPositiveInteger(updatedOrder.id) === normalizedOrderId) {
|
||||||
|
if (mutationSequence === latestOrderMetadataMutationSequence && activeOrderMetadataMutationCount === 1) {
|
||||||
|
applyOrderDetailsToPosState(updatedOrder, response?.data?.includes ?? {});
|
||||||
|
}
|
||||||
|
return applyOrderMetadataFieldToPosState(field, updatedOrder[field] ?? normalizedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyOrderMetadataFieldToPosState(field, normalizedValue);
|
||||||
|
} finally {
|
||||||
|
activeOrderMetadataMutationCount = Math.max(0, activeOrderMetadataMutationCount - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** Get the order details by order id */
|
/** Get the order details by order id */
|
||||||
export const getOrderDetails = async (id = null) => {
|
export const getOrderDetails = async (id = null) => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
@@ -791,6 +974,9 @@ export const getOrderDetails = async (id = null) => {
|
|||||||
if (!token || !targetOrderId) {
|
if (!token || !targetOrderId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const requestId = ++latestOrderDetailsRequestId;
|
||||||
|
const metadataMutationSequenceAtRequest = latestOrderMetadataMutationSequence;
|
||||||
|
const hadActiveMetadataMutationAtRequest = activeOrderMetadataMutationCount > 0;
|
||||||
return axios
|
return axios
|
||||||
.get(API_URL + "/order?id=" + targetOrderId, {
|
.get(API_URL + "/order?id=" + targetOrderId, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -799,54 +985,21 @@ export const getOrderDetails = async (id = null) => {
|
|||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const order_data = response.data.data;
|
const order_data = response.data.data;
|
||||||
const includedCustomer = response?.data?.includes?.customer ?? null;
|
if (!order_data) {
|
||||||
const normalizedOrderCustomerNumber = resolveCustomerNumber(order_data?.customer_id);
|
return null;
|
||||||
const selectedCustomerBeforeHydration = getSelectedCustomerNumber();
|
}
|
||||||
// Set the order details
|
|
||||||
department_id.value = order_data.department_id;
|
|
||||||
reference.value = order_data.reference || "";
|
|
||||||
order_notes.value = order_data.notes || "";
|
|
||||||
order_po.value = order_data.po || "";
|
|
||||||
order_safety_seal.value = normalizeOrderMetadataString(order_data.safety_seal);
|
|
||||||
reg_1.value = order_data.reg_1 || "";
|
|
||||||
reg_2.value = order_data.reg_2 || "";
|
|
||||||
reg_3.value = order_data.reg_3 || "";
|
|
||||||
invoiceCollectionId.value = order_data.invoice_collection_id || null;
|
|
||||||
completed_at.value = order_data.completed_at || null;
|
|
||||||
|
|
||||||
if (includedCustomer && typeof includedCustomer === "object") {
|
const normalizedResponseOrderId = toPositiveInteger(order_data.id ?? targetOrderId);
|
||||||
const customerSource =
|
const normalizedCurrentOrderId = toPositiveInteger(order_id.value);
|
||||||
includedCustomer.economic_customer && typeof includedCustomer.economic_customer === "object"
|
const isStaleForDifferentOrder =
|
||||||
? includedCustomer.economic_customer
|
requestId !== latestOrderDetailsRequestId &&
|
||||||
: includedCustomer;
|
normalizedCurrentOrderId &&
|
||||||
|
normalizedResponseOrderId &&
|
||||||
|
normalizedCurrentOrderId !== normalizedResponseOrderId;
|
||||||
|
const isStaleBeforeMetadataMutation = metadataMutationSequenceAtRequest !== latestOrderMetadataMutationSequence;
|
||||||
|
|
||||||
selectCustomer(
|
if (!isStaleForDifferentOrder && !isStaleBeforeMetadataMutation && !hadActiveMetadataMutationAtRequest) {
|
||||||
normalizeCustomerRecord(customerSource, {
|
applyOrderDetailsToPosState(order_data, response?.data?.includes ?? {});
|
||||||
...includedCustomer,
|
|
||||||
customerNumber: normalizedOrderCustomerNumber ?? resolveCustomerNumber(includedCustomer),
|
|
||||||
name:
|
|
||||||
includedCustomer.name ??
|
|
||||||
includedCustomer.customer_name ??
|
|
||||||
order_data.customer_name ??
|
|
||||||
customer_name.value,
|
|
||||||
customer_name:
|
|
||||||
includedCustomer.customer_name ??
|
|
||||||
includedCustomer.name ??
|
|
||||||
order_data.customer_name ??
|
|
||||||
customer_name.value,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (normalizedOrderCustomerNumber) {
|
|
||||||
customer_id.value = normalizedOrderCustomerNumber;
|
|
||||||
if (
|
|
||||||
selectedCustomerBeforeHydration !== normalizedOrderCustomerNumber ||
|
|
||||||
customer_name.value === "" ||
|
|
||||||
!customer_data.value
|
|
||||||
) {
|
|
||||||
void searchAndSelectCustomer(normalizedOrderCustomerNumber, { forceRefresh: true });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clearSelectedCustomerState();
|
|
||||||
}
|
}
|
||||||
// Return the order data
|
// Return the order data
|
||||||
return order_data;
|
return order_data;
|
||||||
@@ -1338,7 +1491,7 @@ const copyLastWashReferenceToEmptyCurrentOrder = async (sourceReference) => {
|
|||||||
|
|
||||||
const normalizedOrderId = toPositiveInteger(order_id.value);
|
const normalizedOrderId = toPositiveInteger(order_id.value);
|
||||||
if (normalizedOrderId) {
|
if (normalizedOrderId) {
|
||||||
await SessionUser.objects.orders.set.reference(normalizedOrderId, normalizedSourceReference);
|
await saveOrderMetadataField("reference", normalizedSourceReference, normalizedOrderId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1466,24 +1619,7 @@ export const customerUsesPONumbers = () => {
|
|||||||
|
|
||||||
/** Check if the product category is allowed */
|
/** Check if the product category is allowed */
|
||||||
export const productCategoryAllowed = (category) => {
|
export const productCategoryAllowed = (category) => {
|
||||||
// Check if the product category is the addons category
|
return !isProductCategoryRestrictedForCustomer(category, customer_attributes.value);
|
||||||
if (category === "addons") {
|
|
||||||
// Check if the customer has the attribute restrictAdditionalServices
|
|
||||||
return !hasAttribute("restrictAdditionalServices");
|
|
||||||
}
|
|
||||||
if (category === "spot_free") {
|
|
||||||
// Check if the customer has the attribute restrictSpotFree
|
|
||||||
return !hasAttribute("restrictSpotFree");
|
|
||||||
}
|
|
||||||
if (category === "interior_cleaning") {
|
|
||||||
// Check if the customer has the attribute restrictInteriorCleaning
|
|
||||||
return !hasAttribute("restrictInteriorCleaning");
|
|
||||||
}
|
|
||||||
if (category === "tank_cleaning") {
|
|
||||||
// Check if the customer has the attribute restrictTankCleaning
|
|
||||||
return !hasAttribute("restrictTankCleaning");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Check if the customer should be billed per order, or per billing period */
|
/** Check if the customer should be billed per order, or per billing period */
|
||||||
@@ -1529,43 +1665,12 @@ export const hasOnlyTankCleaning = () => {
|
|||||||
|
|
||||||
/** Check if a product is restricted based on customer attributes */
|
/** Check if a product is restricted based on customer attributes */
|
||||||
export const isProductRestricted = (product) => {
|
export const isProductRestricted = (product) => {
|
||||||
if (!product) return false;
|
return isProductRestrictedForCustomer(product, customer_attributes.value);
|
||||||
const productName = (product.name || "").toLowerCase();
|
|
||||||
|
|
||||||
// Check if the product has "Spot Free" in the name
|
|
||||||
if (!canBuySpotFree() && productName.includes("spot free")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the product has "Indvendig vask" in the name
|
|
||||||
if (!canBuyInteriorCleaning() && productName.includes("indvendig vask")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the product category is 5 (Tank Cleaning)
|
|
||||||
if (product.category === 5) {
|
|
||||||
if (!canBuyTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!hasOnlyTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Check if an addon is restricted based on customer attributes */
|
/** Check if an addon is restricted based on customer attributes */
|
||||||
export const isAddonRestricted = (addon) => {
|
export const isAddonRestricted = (addon) => {
|
||||||
if (!addon) return false;
|
return isProductRestrictedForCustomer(addon, customer_attributes.value);
|
||||||
const addonName = (addon.name || "").toLowerCase();
|
|
||||||
|
|
||||||
// Check if the addon has "Spot Free" in the name
|
|
||||||
if (!canBuySpotFree() && addonName.includes("spot free")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the addon has "Indvendig vask" in the name
|
|
||||||
if (!canBuyInteriorCleaning() && addonName.includes("indvendig vask")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function loadOrderDetails(onAfterSuccess = null, options = {}) {
|
export function loadOrderDetails(onAfterSuccess = null, options = {}) {
|
||||||
@@ -1697,13 +1802,30 @@ export const getUserDiscounts = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Get user product discount */
|
const ensureUserDiscountsLoaded = () => {
|
||||||
export const getUserProductDiscount = (product, allowCategory = null, onlyCategory = null) => {
|
|
||||||
// Check if the user discounts are loaded
|
|
||||||
if (!has_user_discounts_loaded.value) {
|
if (!has_user_discounts_loaded.value) {
|
||||||
has_user_discounts_loaded.value = true;
|
has_user_discounts_loaded.value = true;
|
||||||
getUserDiscounts().then(() => {});
|
getUserDiscounts().then(() => {});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDirectCustomerProductDiscount = (discount, productId) => {
|
||||||
|
return discount?.product_or_category_id == productId && Number(discount?.is_category) === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseFixedPriceValue = (value) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = Number.parseInt(String(value), 10);
|
||||||
|
return Number.isFinite(parsed) ? parsed : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Get user product discount */
|
||||||
|
export const getUserProductDiscount = (product, allowCategory = null, onlyCategory = null) => {
|
||||||
|
// Check if the user discounts are loaded
|
||||||
|
ensureUserDiscountsLoaded();
|
||||||
|
|
||||||
// Get the product's id
|
// Get the product's id
|
||||||
const productId = product.id;
|
const productId = product.id;
|
||||||
@@ -1801,6 +1923,36 @@ export const getUserProductDiscount = (product, allowCategory = null, onlyCatego
|
|||||||
return appliedDiscount;
|
return appliedDiscount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getUserProductFixedPrice = (product) => {
|
||||||
|
ensureUserDiscountsLoaded();
|
||||||
|
|
||||||
|
const productId = product?.id;
|
||||||
|
if (productId === null || productId === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixedPriceDiscount = (Array.isArray(user_discounts.value) ? user_discounts.value : []).find((discount) => (
|
||||||
|
isDirectCustomerProductDiscount(discount, productId) && parseFixedPriceValue(discount.fixed_price) !== null
|
||||||
|
));
|
||||||
|
|
||||||
|
return fixedPriceDiscount ? parseFixedPriceValue(fixedPriceDiscount.fixed_price) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserProductPrice = (product) => {
|
||||||
|
const fixedPrice = getUserProductFixedPrice(product);
|
||||||
|
if (fixedPrice !== null) {
|
||||||
|
return fixedPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
const price = Number(product?.price ?? 0);
|
||||||
|
const discount = Number(getUserProductDiscount(product) || 0);
|
||||||
|
if (!Number.isFinite(price)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number((price - (price * (discount / 100))).toFixed(0));
|
||||||
|
};
|
||||||
|
|
||||||
export const showFakeCreateOrderItem = (product, quantity, price) => {
|
export const showFakeCreateOrderItem = (product, quantity, price) => {
|
||||||
order_items.value.push({
|
order_items.value.push({
|
||||||
id: Math.floor(Math.random() * 1000),
|
id: Math.floor(Math.random() * 1000),
|
||||||
@@ -2208,32 +2360,27 @@ export const hydrateSelectedOrderBookingForDesktop = async () => {
|
|||||||
|
|
||||||
const bookingReference = getOrderBookingReferenceValue(booking);
|
const bookingReference = getOrderBookingReferenceValue(booking);
|
||||||
if (bookingReference !== "" && bookingReference !== reference.value) {
|
if (bookingReference !== "" && bookingReference !== reference.value) {
|
||||||
await SessionUser.objects.orders.set.reference(normalizedOrderId, bookingReference);
|
reference.value = await saveOrderMetadataField("reference", bookingReference, normalizedOrderId);
|
||||||
reference.value = bookingReference;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookingReg1 = getOrderBookingReg1Value(booking);
|
const bookingReg1 = getOrderBookingReg1Value(booking);
|
||||||
if (bookingReg1 !== "" && bookingReg1 !== reg_1.value) {
|
if (bookingReg1 !== "" && bookingReg1 !== reg_1.value) {
|
||||||
await SessionUser.objects.orders.set.reg_1(normalizedOrderId, bookingReg1);
|
reg_1.value = await saveOrderMetadataField("reg_1", bookingReg1, normalizedOrderId);
|
||||||
reg_1.value = bookingReg1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookingReg2 = getOrderBookingReg2Value(booking);
|
const bookingReg2 = getOrderBookingReg2Value(booking);
|
||||||
if (bookingReg2 !== reg_2.value) {
|
if (bookingReg2 !== reg_2.value) {
|
||||||
await SessionUser.objects.orders.set.reg_2(normalizedOrderId, bookingReg2);
|
reg_2.value = await saveOrderMetadataField("reg_2", bookingReg2, normalizedOrderId);
|
||||||
reg_2.value = bookingReg2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookingNotes = getOrderBookingNotesValue(booking);
|
const bookingNotes = getOrderBookingNotesValue(booking);
|
||||||
if (bookingNotes !== "" && bookingNotes !== order_notes.value) {
|
if (bookingNotes !== "" && bookingNotes !== order_notes.value) {
|
||||||
await SessionUser.objects.orders.set.notes(normalizedOrderId, bookingNotes);
|
order_notes.value = await saveOrderMetadataField("notes", bookingNotes, normalizedOrderId);
|
||||||
order_notes.value = bookingNotes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookingPo = String(booking?.po ?? "").trim();
|
const bookingPo = String(booking?.po ?? "").trim();
|
||||||
if (bookingPo !== "" && isBlankPosMetadataValue(order_po.value)) {
|
if (bookingPo !== "" && isBlankPosMetadataValue(order_po.value)) {
|
||||||
await SessionUser.objects.orders.set.po(normalizedOrderId, bookingPo);
|
order_po.value = await saveOrderMetadataField("po", bookingPo, normalizedOrderId);
|
||||||
order_po.value = bookingPo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentOrderItems = await loadOrderItems();
|
const currentOrderItems = await loadOrderItems();
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import CollectedInvoiceQueueMonitor from "@/components/viewport/page/headers/men
|
|||||||
import SubuserGrantSelector from "@/components/session/subuser/SubuserGrantSelector.vue";
|
import SubuserGrantSelector from "@/components/session/subuser/SubuserGrantSelector.vue";
|
||||||
import { IS_DEV } from '@/config.js';
|
import { IS_DEV } from '@/config.js';
|
||||||
import SessionUser from "@/components/session/token/SessionUser.vue";
|
import SessionUser from "@/components/session/token/SessionUser.vue";
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
const route = useRoute();
|
import HeaderAccessShortcuts from "@/components/viewport/page/headers/HeaderAccessShortcuts.vue";
|
||||||
const isAuthenticatedSessionReady = computed(() => SessionUser.isInitiated() && SessionUser.authenticated.value);
|
const isAuthenticatedSessionReady = computed(() => SessionUser.isInitiated() && SessionUser.authenticated.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -41,20 +40,7 @@ const isAuthenticatedSessionReady = computed(() => SessionUser.isInitiated() &&
|
|||||||
<!-- User account name and avatar (Desktop) -->
|
<!-- User account name and avatar (Desktop) -->
|
||||||
<div class="navbar-item mr-1">
|
<div class="navbar-item mr-1">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<!-- Back-Office button -->
|
<HeaderAccessShortcuts />
|
||||||
<router-link class="button is-white" to="/superuser" v-if="SessionUser.canAccessSuperUser() && !route.path.includes('/superuser')">
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fas fa-tools" :class="{'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV}"></i>
|
|
||||||
</span>
|
|
||||||
<span>{{ $t('header.backoffice') }}</span>
|
|
||||||
</router-link>
|
|
||||||
<!-- Department button -->
|
|
||||||
<router-link class="button is-white" to="/admin" v-if="SessionUser.canAccessAdmin() && !route.path.includes('/admin')">
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fas fa-building" :class="{'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV}"></i>
|
|
||||||
</span>
|
|
||||||
<span>{{ $t('common.departments') }}</span>
|
|
||||||
</router-link>
|
|
||||||
<!-- Return to own account if impersonating -->
|
<!-- Return to own account if impersonating -->
|
||||||
<template v-if="SessionUser.hasSuperUserToken()">
|
<template v-if="SessionUser.hasSuperUserToken()">
|
||||||
<router-link class="button is-white" to="/auth/return">
|
<router-link class="button is-white" to="/auth/return">
|
||||||
|
|||||||
@@ -63,11 +63,7 @@ const navigationItems = computed<NavigationMenuItem[]>(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const hasPermission = (item: NavigationMenuItem) => {
|
const hasPermission = (item: NavigationMenuItem) => {
|
||||||
if (!item.permissions?.length) {
|
return SessionUser.hasAnyPermission(item.permissions ?? []);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.permissions.some((permission) => SessionUser.hasPermission(permission));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isItemVisible = (item: NavigationMenuItem) => !item.hidden && hasPermission(item);
|
const isItemVisible = (item: NavigationMenuItem) => !item.hidden && hasPermission(item);
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { IS_DEV } from "@/config.js";
|
||||||
|
import SessionUser from "@/components/session/token/SessionUser.vue";
|
||||||
|
import {
|
||||||
|
getAdminDepartmentIdFromRoute,
|
||||||
|
getAdminDepartmentRoute,
|
||||||
|
getLimitedBackofficeDepartmentIdFromRoute,
|
||||||
|
getLimitedBackofficeDepartmentRoute,
|
||||||
|
} from "@/components/viewport/page/headers/headerShortcuts.js";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
|
const adminDepartmentId = computed(() => getAdminDepartmentIdFromRoute(route));
|
||||||
|
const backofficeDepartmentId = computed(() => getLimitedBackofficeDepartmentIdFromRoute(route));
|
||||||
|
|
||||||
|
const canAccessDepartment = (departmentId) =>
|
||||||
|
Number.isInteger(departmentId) && departmentId > 0 && SessionUser.canAccessDepartment(departmentId);
|
||||||
|
|
||||||
|
const canShowSuperuserBackoffice = computed(
|
||||||
|
() => SessionUser.canAccessSuperUser() && !route.path.includes("/superuser")
|
||||||
|
);
|
||||||
|
|
||||||
|
const canShowLimitedBackoffice = computed(() => {
|
||||||
|
const departmentId = adminDepartmentId.value;
|
||||||
|
|
||||||
|
return (
|
||||||
|
canAccessDepartment(departmentId) &&
|
||||||
|
SessionUser.hasPermission("limited_backoffice_access") &&
|
||||||
|
!SessionUser.canAccessSuperUser()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const limitedBackofficeTarget = computed(() => getLimitedBackofficeDepartmentRoute(adminDepartmentId.value));
|
||||||
|
|
||||||
|
const adminShortcutTarget = computed(() =>
|
||||||
|
backofficeDepartmentId.value ? getAdminDepartmentRoute(backofficeDepartmentId.value) : "/admin"
|
||||||
|
);
|
||||||
|
|
||||||
|
const canShowAdminShortcut = computed(() => {
|
||||||
|
if (!SessionUser.canAccessAdmin() || route.path.includes("/admin")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const departmentId = backofficeDepartmentId.value;
|
||||||
|
return departmentId === null || canAccessDepartment(departmentId);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-link
|
||||||
|
v-if="canShowSuperuserBackoffice"
|
||||||
|
class="button is-white"
|
||||||
|
to="/superuser"
|
||||||
|
data-testid="superuser-backoffice-header-button"
|
||||||
|
>
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-tools" :class="{ 'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV }"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ t("header.backoffice") }}</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
v-if="canShowLimitedBackoffice"
|
||||||
|
class="button is-white"
|
||||||
|
:to="limitedBackofficeTarget"
|
||||||
|
data-testid="limited-backoffice-header-button"
|
||||||
|
>
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-tools" :class="{ 'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV }"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ t("templates.limited_backoffice.title") }}</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
v-if="canShowAdminShortcut"
|
||||||
|
class="button is-white"
|
||||||
|
:to="adminShortcutTarget"
|
||||||
|
data-testid="admin-header-button"
|
||||||
|
>
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-building" :class="{ 'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV }"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ t("common.departments") }}</span>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.transform-color-black {
|
||||||
|
filter: invert(100%) grayscale(100%) brightness(0) contrast(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.transform-color-red {
|
||||||
|
filter: invert(27%) sepia(96%) saturate(7493%) hue-rotate(357deg) brightness(103%) contrast(101%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,9 +5,8 @@ import NavigationMenuGlobalSearch from "@/components/viewport/page/headers/menu/
|
|||||||
import CollectedInvoiceQueueMonitor from "@/components/viewport/page/headers/menu/CollectedInvoiceQueueMonitor.vue";
|
import CollectedInvoiceQueueMonitor from "@/components/viewport/page/headers/menu/CollectedInvoiceQueueMonitor.vue";
|
||||||
import { IS_DEV } from '@/config.js';
|
import { IS_DEV } from '@/config.js';
|
||||||
import SessionUser from "@/components/session/token/SessionUser.vue";
|
import SessionUser from "@/components/session/token/SessionUser.vue";
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
const route = useRoute();
|
import HeaderAccessShortcuts from "@/components/viewport/page/headers/HeaderAccessShortcuts.vue";
|
||||||
const isAuthenticatedSessionReady = computed(() => SessionUser.isInitiated() && SessionUser.authenticated.value);
|
const isAuthenticatedSessionReady = computed(() => SessionUser.isInitiated() && SessionUser.authenticated.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -41,20 +40,7 @@ const isAuthenticatedSessionReady = computed(() => SessionUser.isInitiated() &&
|
|||||||
<!-- User account name and avatar (Desktop) -->
|
<!-- User account name and avatar (Desktop) -->
|
||||||
<div class="navbar-item mr-1">
|
<div class="navbar-item mr-1">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<!-- Back-Office button -->
|
<HeaderAccessShortcuts />
|
||||||
<router-link class="button is-white" to="/superuser" v-if="SessionUser.canAccessSuperUser() && !route.path.includes('/superuser')">
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fas fa-tools" :class="{'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV}"></i>
|
|
||||||
</span>
|
|
||||||
<span>{{ $t('header.backoffice') }}</span>
|
|
||||||
</router-link>
|
|
||||||
<!-- Department button -->
|
|
||||||
<router-link class="button is-white" to="/admin" v-if="SessionUser.canAccessAdmin() && !route.path.includes('/admin')">
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fas fa-building" :class="{'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV}"></i>
|
|
||||||
</span>
|
|
||||||
<span>{{ $t('common.departments') }}</span>
|
|
||||||
</router-link>
|
|
||||||
<!-- Return to own account if impersonating -->
|
<!-- Return to own account if impersonating -->
|
||||||
<template v-if="SessionUser.hasSuperUserToken()">
|
<template v-if="SessionUser.hasSuperUserToken()">
|
||||||
<router-link class="button is-white" to="/auth/return">
|
<router-link class="button is-white" to="/auth/return">
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
const parsePositiveInteger = (value) => {
|
||||||
|
const parsedValue = Number.parseInt(String(value ?? ""), 10);
|
||||||
|
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPath = (route) => String(route?.path ?? "");
|
||||||
|
|
||||||
|
export const getAdminDepartmentIdFromRoute = (route) => {
|
||||||
|
const path = getPath(route);
|
||||||
|
const fromPath = parsePositiveInteger(path.match(/^\/admin\/(\d+)(?:\/|$)/)?.[1]);
|
||||||
|
if (fromPath !== null) {
|
||||||
|
return fromPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.startsWith("/admin") ? parsePositiveInteger(route?.params?.departmentId) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLimitedBackofficeDepartmentIdFromRoute = (route) => {
|
||||||
|
const path = getPath(route);
|
||||||
|
const fromPath = parsePositiveInteger(path.match(/^\/backoffice\/departments\/(\d+)(?:\/|$)/)?.[1]);
|
||||||
|
if (fromPath !== null) {
|
||||||
|
return fromPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.startsWith("/backoffice/departments") ? parsePositiveInteger(route?.params?.departmentId) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLimitedBackofficeDepartmentRoute = (departmentId) =>
|
||||||
|
`/backoffice/departments/${encodeURIComponent(String(departmentId))}/prices`;
|
||||||
|
|
||||||
|
export const getAdminDepartmentRoute = (departmentId) => `/admin/${encodeURIComponent(String(departmentId))}`;
|
||||||
@@ -43,23 +43,24 @@ const redirect = async (to: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Permissions and visibility logic
|
// Permissions and visibility logic
|
||||||
const hasPermission = (permission: string) => {
|
|
||||||
return SessionUser.hasPermission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasDepartmentIdInPath = (path: string) => {
|
const hasDepartmentIdInPath = (path: string) => {
|
||||||
const departmentRegex = /\/admin\/\d+/;
|
const departmentRegex = /\/admin\/\d+/;
|
||||||
return departmentRegex.test(path);
|
return departmentRegex.test(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasItemPermissions = (item: any) => {
|
||||||
|
return SessionUser.hasAnyPermission(item?.permissions ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
const hasPermissions = computed(() => {
|
const hasPermissions = computed(() => {
|
||||||
if (!props.item.permissions || props.item.permissions.length === 0) {
|
return hasItemPermissions(props.item);
|
||||||
return true; // No permissions required, item is visible
|
|
||||||
}
|
|
||||||
return props.item.permissions.some(hasPermission);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const isVisible = () => {
|
const isVisible = () => {
|
||||||
|
if (props.item.hidden) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (props.item.type) {
|
switch (props.item.type) {
|
||||||
case 'category':
|
case 'category':
|
||||||
// Check if any child item is visible
|
// Check if any child item is visible
|
||||||
@@ -96,10 +97,14 @@ const hasChildren = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isChildVisible = (child: any) => {
|
const isChildVisible = (child: any) => {
|
||||||
if (child.type === 'department') {
|
if (child.hidden) {
|
||||||
return hasPermissions.value && hasDepartmentIdInPath(router.currentRoute.value.path);
|
return false;
|
||||||
}
|
}
|
||||||
return hasPermissions.value;
|
|
||||||
|
if (child.type === 'department') {
|
||||||
|
return hasItemPermissions(child) && hasDepartmentIdInPath(router.currentRoute.value.path);
|
||||||
|
}
|
||||||
|
return hasItemPermissions(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasVisibleChildren = computed(() => {
|
const hasVisibleChildren = computed(() => {
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ const descriptionKeyOverrides: Partial<Record<EntityType, string[]>> = {
|
|||||||
xlvask_customers: ['email', 'city', 'address', 'vatnumber'],
|
xlvask_customers: ['email', 'city', 'address', 'vatnumber'],
|
||||||
motorapi_lookups: ['endpoint', 'license_plate', 'result'],
|
motorapi_lookups: ['endpoint', 'license_plate', 'result'],
|
||||||
fxratesapi_conversion_rates: ['base', 'target', 'endpoint', 'result'],
|
fxratesapi_conversion_rates: ['base', 'target', 'endpoint', 'result'],
|
||||||
customer_discounts: ['customer_number', 'percentage'],
|
customer_discounts: ['customer_number', 'fixed_price', 'percentage'],
|
||||||
module_config: ['description', 'value'],
|
module_config: ['description', 'value'],
|
||||||
department_goals: ['criteria.type', 'criteria.progress_alert_frequency', 'criteria.progress_alert_destination']
|
department_goals: ['criteria.type', 'criteria.progress_alert_frequency', 'criteria.progress_alert_destination']
|
||||||
};
|
};
|
||||||
@@ -251,6 +251,7 @@ const keyFieldOverrides: Partial<Record<EntityType, FieldSelector[]>> = {
|
|||||||
),
|
),
|
||||||
customer_discounts: fieldList(
|
customer_discounts: fieldList(
|
||||||
field('Discount', 'discount', 'percentage'),
|
field('Discount', 'discount', 'percentage'),
|
||||||
|
field('Fixed price', 'fixed_price'),
|
||||||
field('Target', 'product_or_category_id', 'object_id'),
|
field('Target', 'product_or_category_id', 'object_id'),
|
||||||
field('Category', 'is_category'),
|
field('Category', 'is_category'),
|
||||||
field('Customer #', 'customer_number'),
|
field('Customer #', 'customer_number'),
|
||||||
|
|||||||
@@ -23,10 +23,12 @@ export const useOrderMetadataAutosave = ({
|
|||||||
const draft = ref(normalizeValue(resolveSourceValue(source)));
|
const draft = ref(normalizeValue(resolveSourceValue(source)));
|
||||||
const lastSavedValue = ref(normalizeValue(resolveSourceValue(source)));
|
const lastSavedValue = ref(normalizeValue(resolveSourceValue(source)));
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
|
const lastError = ref(null);
|
||||||
const isDirty = computed(() => normalizeValue(draft.value) !== normalizeValue(lastSavedValue.value));
|
const isDirty = computed(() => normalizeValue(draft.value) !== normalizeValue(lastSavedValue.value));
|
||||||
|
|
||||||
let timerId = null;
|
let timerId = null;
|
||||||
let saveAgainAfterCurrentRequest = false;
|
let saveAgainAfterCurrentRequest = false;
|
||||||
|
let activeSavePromise = null;
|
||||||
let nextSequence = 0;
|
let nextSequence = 0;
|
||||||
let lastAppliedSequence = 0;
|
let lastAppliedSequence = 0;
|
||||||
|
|
||||||
@@ -66,45 +68,57 @@ export const useOrderMetadataAutosave = ({
|
|||||||
const sequence = ++nextSequence;
|
const sequence = ++nextSequence;
|
||||||
let saveCompleted = false;
|
let saveCompleted = false;
|
||||||
isSaving.value = true;
|
isSaving.value = true;
|
||||||
|
lastError.value = null;
|
||||||
|
|
||||||
try {
|
const run = (async () => {
|
||||||
const result = await saveValue(requestedValue);
|
try {
|
||||||
const savedValue = normalizeValue(result ?? requestedValue);
|
const result = await saveValue(requestedValue);
|
||||||
|
const savedValue = normalizeValue(result ?? requestedValue);
|
||||||
|
|
||||||
if (sequence >= lastAppliedSequence) {
|
if (sequence >= lastAppliedSequence) {
|
||||||
lastAppliedSequence = sequence;
|
lastAppliedSequence = sequence;
|
||||||
lastSavedValue.value = savedValue;
|
lastSavedValue.value = savedValue;
|
||||||
|
|
||||||
if (normalizeValue(draft.value) === requestedValue) {
|
if (normalizeValue(draft.value) === requestedValue) {
|
||||||
draft.value = savedValue;
|
draft.value = savedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof onSaved === "function") {
|
||||||
|
await onSaved(savedValue, requestedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveCompleted = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
lastError.value = error;
|
||||||
|
if (typeof onError === "function") {
|
||||||
|
onError(error);
|
||||||
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
isSaving.value = false;
|
||||||
|
|
||||||
|
const shouldSaveAgain = saveAgainAfterCurrentRequest;
|
||||||
|
saveAgainAfterCurrentRequest = false;
|
||||||
|
|
||||||
|
if (saveCompleted && (shouldSaveAgain || isDirty.value) && isDirty.value) {
|
||||||
|
void persist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
if (typeof onSaved === "function") {
|
const trackedSavePromise = run.finally(() => {
|
||||||
await onSaved(savedValue, requestedValue);
|
if (activeSavePromise === trackedSavePromise) {
|
||||||
|
activeSavePromise = null;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
saveCompleted = true;
|
activeSavePromise = trackedSavePromise;
|
||||||
|
return trackedSavePromise;
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
if (typeof onError === "function") {
|
|
||||||
onError(error);
|
|
||||||
} else {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
isSaving.value = false;
|
|
||||||
|
|
||||||
const shouldSaveAgain = saveAgainAfterCurrentRequest;
|
|
||||||
saveAgainAfterCurrentRequest = false;
|
|
||||||
|
|
||||||
if (saveCompleted && (shouldSaveAgain || isDirty.value) && isDirty.value) {
|
|
||||||
void persist();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const scheduleSave = () => {
|
const scheduleSave = () => {
|
||||||
@@ -123,6 +137,34 @@ export const useOrderMetadataAutosave = ({
|
|||||||
return persist();
|
return persist();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const flushAndWait = async () => {
|
||||||
|
clearPendingSave();
|
||||||
|
|
||||||
|
while (isSaving.value && activeSavePromise) {
|
||||||
|
await activeSavePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDirty.value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const didSave = await persist();
|
||||||
|
|
||||||
|
if (!didSave && isDirty.value) {
|
||||||
|
throw lastError.value || new Error("Unable to save order metadata");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isSaving.value && activeSavePromise) {
|
||||||
|
await activeSavePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDirty.value) {
|
||||||
|
return flushAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
return didSave;
|
||||||
|
};
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearPendingSave();
|
clearPendingSave();
|
||||||
|
|
||||||
@@ -139,10 +181,12 @@ export const useOrderMetadataAutosave = ({
|
|||||||
return {
|
return {
|
||||||
draft,
|
draft,
|
||||||
lastSavedValue,
|
lastSavedValue,
|
||||||
|
lastError,
|
||||||
isDirty,
|
isDirty,
|
||||||
isSaving,
|
isSaving,
|
||||||
scheduleSave,
|
scheduleSave,
|
||||||
flush,
|
flush,
|
||||||
|
flushAndWait,
|
||||||
clearPendingSave,
|
clearPendingSave,
|
||||||
syncFromSource,
|
syncFromSource,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { computed, onBeforeUnmount, reactive, ref, watch } from "vue";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { searchAndSelectCustomer } from "@/components/shop/POSDepartmentProcess.vue";
|
import { searchAndSelectCustomer } from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import SessionUser from "@/components/session/token/SessionUser.vue";
|
import SessionUser from "@/components/session/token/SessionUser.vue";
|
||||||
|
import { economicEanPayloadValue } from "@/services/economicCustomerIdentifiers.js";
|
||||||
import {
|
import {
|
||||||
applyPosAddCustomerSearchResult,
|
applyPosAddCustomerSearchResult,
|
||||||
createPosAddCustomerFields,
|
createPosAddCustomerFields,
|
||||||
@@ -152,14 +153,20 @@ export function usePosAddCustomerForm(options = {}) {
|
|||||||
errorMessage.value = null;
|
errorMessage.value = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await SessionUser.request("/auth/register/cvr", "POST", {
|
const payload = {
|
||||||
cvr: form.cvr,
|
cvr: form.cvr,
|
||||||
companyPhone: Number.parseInt(form.companyPhone, 10),
|
companyPhone: Number.parseInt(form.companyPhone, 10),
|
||||||
invoiceEmail: form.invoiceEmail,
|
invoiceEmail: form.invoiceEmail,
|
||||||
contactEmail: form.contactEmail,
|
contactEmail: form.contactEmail,
|
||||||
contactPhone: Number.parseInt(form.contactPhone, 10),
|
contactPhone: Number.parseInt(form.contactPhone, 10),
|
||||||
searchResult: searchResult.value,
|
searchResult: searchResult.value,
|
||||||
});
|
};
|
||||||
|
const ean = economicEanPayloadValue(form.ean);
|
||||||
|
if (ean !== undefined) {
|
||||||
|
payload.ean = ean;
|
||||||
|
}
|
||||||
|
|
||||||
|
await SessionUser.request("/auth/register/cvr", "POST", payload);
|
||||||
|
|
||||||
const selectedCustomer = await searchAndSelectCustomer(form.companyPhone);
|
const selectedCustomer = await searchAndSelectCustomer(form.companyPhone);
|
||||||
const createdCustomerNumber = Number.parseInt(form.companyPhone, 10);
|
const createdCustomerNumber = Number.parseInt(form.companyPhone, 10);
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
const TANK_CLEANING_TERMS = ["tank cleaning", "tankcleaning", "tankrens"];
|
||||||
|
|
||||||
|
const normalizeAttributeName = (attribute) => {
|
||||||
|
if (typeof attribute === "string") {
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(attribute?.attribute ?? "");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hasCustomerAttribute = (attributes, attributeName) => {
|
||||||
|
if (!Array.isArray(attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes.some((attribute) => normalizeAttributeName(attribute) === attributeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizedText = (value) => String(value ?? "").trim().toLowerCase();
|
||||||
|
|
||||||
|
const textContainsAny = (value, terms) => {
|
||||||
|
const text = normalizedText(value);
|
||||||
|
return terms.some((term) => term !== "" && text.includes(term));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isTankCleaningCategory = (category) => {
|
||||||
|
if (Number(category) === 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textContainsAny(category, ["tank_cleaning", ...TANK_CLEANING_TERMS]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isTankCleaningProduct = (product) => {
|
||||||
|
if (!product) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTankCleaningCategory(product.category ?? product.product_category)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textContainsAny(
|
||||||
|
[
|
||||||
|
product.name,
|
||||||
|
product.product_name,
|
||||||
|
product.category_name,
|
||||||
|
product.categoryName,
|
||||||
|
].join(" "),
|
||||||
|
TANK_CLEANING_TERMS
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isProductCategoryRestrictedForCustomer = (category, attributes = []) => {
|
||||||
|
if (category === "addons" && hasCustomerAttribute(attributes, "restrictAdditionalServices")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (category === "spot_free" && hasCustomerAttribute(attributes, "restrictSpotFree")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (category === "interior_cleaning" && hasCustomerAttribute(attributes, "restrictInteriorCleaning")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isTankCleaningCategory(category) && hasCustomerAttribute(attributes, "restrictTankCleaning")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasCustomerAttribute(attributes, "onlyTankCleaning") && !isTankCleaningCategory(category)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isProductRestrictedForCustomer = (product, attributes = []) => {
|
||||||
|
if (!product) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const productName = normalizedText(product.name ?? product.product_name);
|
||||||
|
|
||||||
|
if (hasCustomerAttribute(attributes, "restrictSpotFree") && productName.includes("spot free")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasCustomerAttribute(attributes, "restrictInteriorCleaning") && productName.includes("indvendig vask")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isTankCleaningProduct(product) && hasCustomerAttribute(attributes, "restrictTankCleaning")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasCustomerAttribute(attributes, "onlyTankCleaning") && !isTankCleaningProduct(product)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
@@ -1176,6 +1176,9 @@
|
|||||||
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
||||||
|
"autosave_saving": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
|
"autosave_pending": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
|
"autosave_saved": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
||||||
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
||||||
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
||||||
@@ -1190,6 +1193,15 @@
|
|||||||
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'templates.generated.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'templates.generated.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'templates.generated.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -1208,7 +1220,13 @@
|
|||||||
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'templates.generated.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'templates.generated.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -1238,7 +1256,132 @@
|
|||||||
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'templates.generated.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'templates.generated.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'templates.generated.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'templates.generated.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'templates.generated.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'templates.generated.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'templates.generated.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'templates.generated.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -1568,6 +1711,7 @@
|
|||||||
"company_phone": "Virksomhedstelefon",
|
"company_phone": "Virksomhedstelefon",
|
||||||
"contact_email": "@.capitalize:{'words.generated.kontakt'}-@:{'words.generated.e'}-@:{'words.generated.mail'}",
|
"contact_email": "@.capitalize:{'words.generated.kontakt'}-@:{'words.generated.e'}-@:{'words.generated.mail'}",
|
||||||
"contact_phone": "Kontakttelefon",
|
"contact_phone": "Kontakttelefon",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'words.generated.oprettet'} @:{'words.generated.af'}",
|
"created_by": "@.capitalize:{'words.generated.oprettet'} @:{'words.generated.af'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'words.replication.article.host_mention'} @:{'words.generated.indtastede'} @:{'words.generated.nummerplade'} @:{'words.generated.tilhører'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.anden'} @:{'words.generated.gemt'} @:{'words.generated.kunde'}. @.capitalize:{'words.generated.vælg'} @:{'words.generated.om'} @:{'words.replication.article.host_mention'} @:{'words.generated.valgte'} @:{'words.generated.kunde'} @:{'words.generated.skal'} @:{'words.generated.beholdes'}, @:{'words.generated.eller'} @:{'words.generated.om'} @:{'words.generated.der'} @:{'words.generated.skal'} @:{'words.generated.skiftes'} @:{'words.generated.til'} @:{'words.generated.køretøjets'} @:{'words.generated.kunde'}.",
|
"help_text": "@.capitalize:{'words.replication.article.host_mention'} @:{'words.generated.indtastede'} @:{'words.generated.nummerplade'} @:{'words.generated.tilhører'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.anden'} @:{'words.generated.gemt'} @:{'words.generated.kunde'}. @.capitalize:{'words.generated.vælg'} @:{'words.generated.om'} @:{'words.replication.article.host_mention'} @:{'words.generated.valgte'} @:{'words.generated.kunde'} @:{'words.generated.skal'} @:{'words.generated.beholdes'}, @:{'words.generated.eller'} @:{'words.generated.om'} @:{'words.generated.der'} @:{'words.generated.skal'} @:{'words.generated.skiftes'} @:{'words.generated.til'} @:{'words.generated.køretøjets'} @:{'words.generated.kunde'}.",
|
||||||
@@ -3116,6 +3260,7 @@
|
|||||||
"benefit_portal": "@:{'words.generated.kundeportal'}",
|
"benefit_portal": "@:{'words.generated.kundeportal'}",
|
||||||
"benefit_portal_desc": "@:{'words.generated.benyt'} kundeportalen @:{'words.generated.til'} @:{'words.generated.at'} @:{'words.generated.tilføje'} @:{'words.generated.køretøjer'}, @:{'words.generated.booke'} @:{'words.generated.vaske'}, downloade @:{'words.generated.fakturaer'}, @:{'words.generated.vaskecertifikater'} @:{'words.generated.og'} @:{'words.generated.information'} @:{'words.generated.for'} @:{'words.generated.historiske'} @:{'words.generated.vaske'}",
|
"benefit_portal_desc": "@:{'words.generated.benyt'} kundeportalen @:{'words.generated.til'} @:{'words.generated.at'} @:{'words.generated.tilføje'} @:{'words.generated.køretøjer'}, @:{'words.generated.booke'} @:{'words.generated.vaske'}, downloade @:{'words.generated.fakturaer'}, @:{'words.generated.vaskecertifikater'} @:{'words.generated.og'} @:{'words.generated.information'} @:{'words.generated.for'} @:{'words.generated.historiske'} @:{'words.generated.vaske'}",
|
||||||
"benefit_wash_desc": "@:{'words.generated.benyt'} @:{'words.generated.vores'} @:{'words.generated.nationale'} @:{'words.generated.netværk'} @:{'words.generated.af'} @:{'words.generated.vaskehaller'} @:{'words.generated.til'} @:{'words.generated.konkurrencedygtige'} @:{'words.generated.priser'}",
|
"benefit_wash_desc": "@:{'words.generated.benyt'} @:{'words.generated.vores'} @:{'words.generated.nationale'} @:{'words.generated.netværk'} @:{'words.generated.af'} @:{'words.generated.vaskehaller'} @:{'words.generated.til'} @:{'words.generated.konkurrencedygtige'} @:{'words.generated.priser'}",
|
||||||
|
"ean_label": "@:{'templates.generated.compat.admin.pos.ean'}",
|
||||||
"email_label": "@:{'words.generated.firma'} @:{'words.generated.email'}",
|
"email_label": "@:{'words.generated.firma'} @:{'words.generated.email'}",
|
||||||
"intro": "@.capitalize:{'words.generated.som'} @:{'words.generated.kunde'} @:{'words.generated.hos'} @:{'words.generated.truck'} @:{'words.generated.wash'} @:{'words.generated.far'} @:{'words.generated.du'} @:{'words.generated.adgang'} @:{'words.generated.til'}:",
|
"intro": "@.capitalize:{'words.generated.som'} @:{'words.generated.kunde'} @:{'words.generated.hos'} @:{'words.generated.truck'} @:{'words.generated.wash'} @:{'words.generated.far'} @:{'words.generated.du'} @:{'words.generated.adgang'} @:{'words.generated.til'}:",
|
||||||
"phone_label": "@:{'words.generated.firma'} @:{'words.generated.telefon'}",
|
"phone_label": "@:{'words.generated.firma'} @:{'words.generated.telefon'}",
|
||||||
@@ -3331,6 +3476,13 @@
|
|||||||
"opening_hours": {
|
"opening_hours": {
|
||||||
"closed": "Stengt"
|
"closed": "Stengt"
|
||||||
},
|
},
|
||||||
|
"pricing": {
|
||||||
|
"custom_pricing_disabled": "Fallback aktiv",
|
||||||
|
"custom_pricing_enabled": "Kun egne priser",
|
||||||
|
"custom_pricing_missing_price": "Manglende afdelingspriser bliver 999999.",
|
||||||
|
"custom_pricing_only": "Ingen fallback-priser",
|
||||||
|
"effective_department_price": "Effektiv afdelingspris"
|
||||||
|
},
|
||||||
"search_placeholder": "@.capitalize:{'words.generated.søg'} @:{'words.generated.efter'} @:{'words.generated.afdelingsnavn'}",
|
"search_placeholder": "@.capitalize:{'words.generated.søg'} @:{'words.generated.efter'} @:{'words.generated.afdelingsnavn'}",
|
||||||
"select_department": "@.capitalize:{'words.generated.vælg'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.afdeling'}",
|
"select_department": "@.capitalize:{'words.generated.vælg'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.afdeling'}",
|
||||||
"subtitle": "@.capitalize:{'words.generated.administrer'} @:{'words.generated.afdelinger'}",
|
"subtitle": "@.capitalize:{'words.generated.administrer'} @:{'words.generated.afdelinger'}",
|
||||||
@@ -3354,17 +3506,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Rapporter fejl",
|
"button": "Rapporter fejl",
|
||||||
"title": "Rapporter fejl",
|
"title": "Rapporter fejl",
|
||||||
"subtitle": "Send det aktuelle skærmbillede og de seneste fejldetaljer til support.",
|
"subtitle": "Send de seneste fejldetaljer til support. Et skærmbillede vedhæftes, når det er muligt.",
|
||||||
"before_error": "Hvad lavede du, før fejlen opstod?",
|
"before_error": "Hvad lavede du, før fejlen opstod?",
|
||||||
"expected": "Hvad forventede du, der ville ske?",
|
"expected": "Hvad forventede du, der ville ske?",
|
||||||
"actual": "Hvad skete der faktisk?",
|
"actual": "Hvad skete der faktisk?",
|
||||||
"before_error_placeholder": "Beskriv handlingen, du var i gang med, for eksempel at åbne ordrer eller vælge en kunde.",
|
"before_error_placeholder": "Beskriv handlingen, du var i gang med, for eksempel at åbne ordrer eller vælge en kunde.",
|
||||||
"expected_placeholder": "Beskriv det resultat, du forventede at se.",
|
"expected_placeholder": "Beskriv det resultat, du forventede at se.",
|
||||||
"actual_placeholder": "Beskriv, hvad du så i stedet, inklusive eventuel fejltekst.",
|
"actual_placeholder": "Beskriv, hvad du så i stedet, inklusive eventuel fejltekst.",
|
||||||
"consent": "Jeg accepterer, at det aktuelle app-skærmbillede, de seneste request-fejl, Vue-fejl, browseroplysninger og mine svar indsamles til fejlfinding.",
|
"consent": "Jeg accepterer, at de seneste request-fejl, Vue-fejl, browseroplysninger, mine svar og et app-skærmbillede, når det er muligt, indsamles til fejlfinding.",
|
||||||
"submit": "Send rapport",
|
"submit": "Send rapport",
|
||||||
"submitted": "Fejlrapporten er sendt.",
|
"submitted": "Fejlrapporten er sendt.",
|
||||||
"capture_failed": "Skærmbilledet kunne ikke oprettes. Prøv igen.",
|
"capture_failed": "Skærmbilledet kunne ikke oprettes. Rapporten sendes uden skærmbillede.",
|
||||||
"submit_failed": "Fejlrapporten kunne ikke sendes.",
|
"submit_failed": "Fejlrapporten kunne ikke sendes.",
|
||||||
"required": "Alle felter og accept af dataindsamling er påkrævet.",
|
"required": "Alle felter og accept af dataindsamling er påkrævet.",
|
||||||
"page_subtitle": "Rapporterede skærme, request-fejl og Vue-fejl",
|
"page_subtitle": "Rapporterede skærme, request-fejl og Vue-fejl",
|
||||||
@@ -3904,6 +4056,15 @@
|
|||||||
"preview_title": "@:{'words.generated.forhandsvis'} @:{'words.generated.manedsopdeling'}",
|
"preview_title": "@:{'words.generated.forhandsvis'} @:{'words.generated.manedsopdeling'}",
|
||||||
"success_text": "@:{'words.generated.behandlede'} {processed} @:{'words.generated.fakturasamlinger'}. Opdelte {changed} @:{'words.generated.og'} sprang {skipped} @:{'words.generated.over'}.",
|
"success_text": "@:{'words.generated.behandlede'} {processed} @:{'words.generated.fakturasamlinger'}. Opdelte {changed} @:{'words.generated.og'} sprang {skipped} @:{'words.generated.over'}.",
|
||||||
"success_title": "@.capitalize:{'words.generated.manedsopdeling'} @:{'words.generated.fuldført'}"
|
"success_title": "@.capitalize:{'words.generated.manedsopdeling'} @:{'words.generated.fuldført'}"
|
||||||
|
},
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Fakturer sammen",
|
||||||
|
"split_by_month": "Opdel efter måned",
|
||||||
|
"split_error_title": "Månedsopdeling mislykkedes",
|
||||||
|
"split_success_text": "Behandlede {processed} fakturasamlinger. Opdelte {changed} og sprang {skipped} over.",
|
||||||
|
"split_success_title": "Månedsopdeling fuldført",
|
||||||
|
"text": "Du er ved at fakturere ordrer fra flere måneder sammen ({months}). Skal de i stedet opdeles efter måned?",
|
||||||
|
"title": "Ordrer fra flere måneder"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoicing": {
|
"invoicing": {
|
||||||
@@ -4161,6 +4322,7 @@
|
|||||||
"departments": {
|
"departments": {
|
||||||
"columns": {
|
"columns": {
|
||||||
"archived": "Arkiveret",
|
"archived": "Arkiveret",
|
||||||
|
"custom_pricing_only": "Ingen fallback-priser",
|
||||||
"dimension": "Dimension",
|
"dimension": "Dimension",
|
||||||
"economic_department": "@.upper:{'words.generated.e'}-@:{'words.generated.conomic'} @:{'words.generated.afdeling'}",
|
"economic_department": "@.upper:{'words.generated.e'}-@:{'words.generated.conomic'} @:{'words.generated.afdeling'}",
|
||||||
"latitude": "@:{'templates.generated.compat.departments.form.latitude'}",
|
"latitude": "@:{'templates.generated.compat.departments.form.latitude'}",
|
||||||
@@ -4722,6 +4884,97 @@
|
|||||||
"subtitle": "@.capitalize:{'words.generated.administreret'} @:{'words.replication.services.database'}-, @:{'words.replication.services.redis'}- @:{'words.generated.og'} @:{'words.replication.services.minio'}-@:{'words.generated.replikering'}"
|
"subtitle": "@.capitalize:{'words.generated.administreret'} @:{'words.replication.services.database'}-, @:{'words.replication.services.redis'}- @:{'words.generated.og'} @:{'words.replication.services.minio'}-@:{'words.generated.replikering'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Adgangsomrade",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Ryd gruppe",
|
||||||
|
"reload": "Genindlaes",
|
||||||
|
"retry": "Prov igen",
|
||||||
|
"select_all": "Vaelg gruppe"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Aktiveret",
|
||||||
|
"permission": "Tilladelse",
|
||||||
|
"route": "Rute",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Giver adgang til {name}.",
|
||||||
|
"empty": "Der er ingen tilladelser tilgaengelige.",
|
||||||
|
"empty_group": "Denne gruppe har ingen tilladelser.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Rolletilladelser kunne ikke indlaeses.",
|
||||||
|
"update": "En eller flere tilladelser kunne ikke opdateres. Rollens aktuelle tilladelser blev genindlaest."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} matcher",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Sidetype- og afdelingsadgang.",
|
||||||
|
"label": "Adgang"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Tilladelser til bookinger, tidsbookinger og planlaegning.",
|
||||||
|
"label": "Bookinger"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Tilladelser til kunder, brugere, medarbejdere, roller og login.",
|
||||||
|
"label": "Kunder og brugere"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Afdelingsdrift, baner, relaer, notifikationer, rapporter og mal.",
|
||||||
|
"label": "Afdelinger og drift"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Tilladelser til fakturaer, faktureringsperioder, opkraevning og e-conomic.",
|
||||||
|
"label": "Fakturering og e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Tilladelser til ordrer, POS, betalinger og ordrelinjer.",
|
||||||
|
"label": "Ordrer og POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Tilladelser, der ikke matcher en anden gruppe.",
|
||||||
|
"label": "Andet"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Tilladelser til produkter, priser, rabatter og valuta.",
|
||||||
|
"label": "Produkter og priser"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Tilladelser til selvbetjening, vask, scannere, gateways og edge-udstyr.",
|
||||||
|
"label": "Selvbetjening og edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, konfiguration, release, replikering og integrationer.",
|
||||||
|
"label": "System og konfiguration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "Der er ingen beskrivelse.",
|
||||||
|
"no_search_results": "Ingen tilladelser matcher sogningen.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Giver adgang til administrationssider.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Giver adgang til superbrugersider.",
|
||||||
|
"label": "Superbruger"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Giver adgang til brugersider.",
|
||||||
|
"label": "Bruger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Sog i tilladelser",
|
||||||
|
"search_placeholder": "Sog pa label, nogle, beskrivelse, rute eller metode",
|
||||||
|
"selected_summary": "{selected} af {total} valgt",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Deaktiveret",
|
||||||
|
"enabled": "Aktiveret"
|
||||||
|
},
|
||||||
|
"subtitle": "Rolletilladelser",
|
||||||
|
"title": "Tilladelser for rolle {id}",
|
||||||
|
"title_with_name": "Tilladelser for {name}"
|
||||||
|
},
|
||||||
"subtitle": "@.capitalize:{'words.generated.brugerroller'}"
|
"subtitle": "@.capitalize:{'words.generated.brugerroller'}"
|
||||||
},
|
},
|
||||||
"self_wash": {
|
"self_wash": {
|
||||||
@@ -5511,6 +5764,31 @@
|
|||||||
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
||||||
},
|
},
|
||||||
"other": "@:{'templates.generated.compat.global.other'}",
|
"other": "@:{'templates.generated.compat.global.other'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Aktive køretøjer",
|
||||||
|
"attributes": "Attributter",
|
||||||
|
"connected": "Forbundet",
|
||||||
|
"customer_management": "Kundestyring",
|
||||||
|
"customer_number_required": "Et kundenummer er påkrævet før kundeindstillinger kan administreres.",
|
||||||
|
"customer_rules": "Kunderegler",
|
||||||
|
"effective_access": "Effektiv adgang",
|
||||||
|
"loaded": "Indlæst",
|
||||||
|
"load_failed": "Brugerdata kunne ikke indlæses",
|
||||||
|
"loading_user": "Indlæser bruger",
|
||||||
|
"management_hub": "Administrationshub",
|
||||||
|
"missing": "Mangler",
|
||||||
|
"more_permissions": "+{count} flere tilladelser",
|
||||||
|
"no_subscription_transactions": "Der er ikke registreret vaskeabonnementstransaktioner for kunden.",
|
||||||
|
"not_invoiced": "Ikke faktureret",
|
||||||
|
"open_draft": "Åben kladde",
|
||||||
|
"open_vehicles": "Åbn køretøjer",
|
||||||
|
"price_overrides": "Prisoverstyringer",
|
||||||
|
"pricing": "Priser",
|
||||||
|
"subscription_invoicing": "Abonnementsfakturering",
|
||||||
|
"subtitle": "Overblik over kundedata, adgang, priser, køretøjer og abonnementsfakturering.",
|
||||||
|
"vehicles_load_failed": "Køretøjer kunne ikke indlæses.",
|
||||||
|
"workspace_shortcuts": "Arbejdsgange"
|
||||||
|
},
|
||||||
"permissions": "@.capitalize:{'words.generated.tilladelser'}",
|
"permissions": "@.capitalize:{'words.generated.tilladelser'}",
|
||||||
"user_data": "@.capitalize:{'words.generated.brugerdata'}",
|
"user_data": "@.capitalize:{'words.generated.brugerdata'}",
|
||||||
"user_id": "@.capitalize:{'words.generated.bruger'} @.upper:{'words.generated.id'}",
|
"user_id": "@.capitalize:{'words.generated.bruger'} @.upper:{'words.generated.id'}",
|
||||||
@@ -5717,6 +5995,20 @@
|
|||||||
},
|
},
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"add": "@:{'words.generated.tilføj'} @:{'words.generated.køretøj'}",
|
"add": "@:{'words.generated.tilføj'} @:{'words.generated.køretøj'}",
|
||||||
|
"add_modal": {
|
||||||
|
"customer_label": "Kunde",
|
||||||
|
"customer_placeholder": "Søg efter kundenavn eller kundenummer",
|
||||||
|
"error": "Køretøjet kunne ikke tilføjes.",
|
||||||
|
"no_customer_results": "Ingen kunder fundet",
|
||||||
|
"reference_placeholder": "Valgfri reference",
|
||||||
|
"registration_placeholder": "Registreringsnummer",
|
||||||
|
"selected_customer": "Valgt kunde",
|
||||||
|
"submit": "Tilføj køretøj",
|
||||||
|
"title": "Tilføj køretøj",
|
||||||
|
"type_load_error": "Køretøjstyper kunne ikke indlæses.",
|
||||||
|
"type_placeholder": "Vælg køretøjstype",
|
||||||
|
"validation_error": "Vælg kunde, registreringsnummer og køretøjstype."
|
||||||
|
},
|
||||||
"brand": "@:{'templates.generated.compat.admin.pos.make'}",
|
"brand": "@:{'templates.generated.compat.admin.pos.make'}",
|
||||||
"color": "Farve",
|
"color": "Farve",
|
||||||
"delete_vehicle": "@:{'templates.generated.compat.vehicles.delete'}",
|
"delete_vehicle": "@:{'templates.generated.compat.vehicles.delete'}",
|
||||||
@@ -5781,6 +6073,9 @@
|
|||||||
"title": "@.capitalize:{'words.generated.afdelingspriser'}",
|
"title": "@.capitalize:{'words.generated.afdelingspriser'}",
|
||||||
"save": "@:{'words.generated.gem'} @:{'words.generated.priser'}",
|
"save": "@:{'words.generated.gem'} @:{'words.generated.priser'}",
|
||||||
"saved": "Priserne @:{'words.generated.er'} @:{'words.generated.gemt'}.",
|
"saved": "Priserne @:{'words.generated.er'} @:{'words.generated.gemt'}.",
|
||||||
|
"autosave_saving": "Gemmer priser...",
|
||||||
|
"autosave_pending": "Prisændringer afventer.",
|
||||||
|
"autosave_saved": "Alle prisændringer er gemt.",
|
||||||
"product": "Produkt",
|
"product": "Produkt",
|
||||||
"price": "Afdelingspris",
|
"price": "Afdelingspris",
|
||||||
"price_required": "@.capitalize:{'words.generated.alle'} @:{'words.generated.produkter'} @:{'words.generated.skal'} have @:{'words.replication.host_definite_suffix'} @:{'words.generated.ikke'}-negativ numerisk pris.",
|
"price_required": "@.capitalize:{'words.generated.alle'} @:{'words.generated.produkter'} @:{'words.generated.skal'} have @:{'words.replication.host_definite_suffix'} @:{'words.generated.ikke'}-negativ numerisk pris.",
|
||||||
@@ -5795,6 +6090,15 @@
|
|||||||
"edit_title": "@:{'words.generated.rediger'} @:{'words.generated.medarbejder'}",
|
"edit_title": "@:{'words.generated.rediger'} @:{'words.generated.medarbejder'}",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"user_id": "Bruger-id",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"phone_validation": "Telefonnummeret skal være 4-15 cifre.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Danmark",
|
||||||
|
"sweden": "Sverige",
|
||||||
|
"norway": "Norge",
|
||||||
|
"finland": "Finland"
|
||||||
|
},
|
||||||
"password": "@.capitalize:{'words.generated.adgangskode'}",
|
"password": "@.capitalize:{'words.generated.adgangskode'}",
|
||||||
"password_create_help": "@.capitalize:{'words.generated.pakrævet'} @:{'words.generated.for'} nye @:{'words.generated.medarbejdere'}. Brug @:{'words.generated.mindst'} 8 tegn.",
|
"password_create_help": "@.capitalize:{'words.generated.pakrævet'} @:{'words.generated.for'} nye @:{'words.generated.medarbejdere'}. Brug @:{'words.generated.mindst'} 8 tegn.",
|
||||||
"password_update_help": "Lad feltet være tomt @:{'words.generated.for'} at beholde den nuværende @:{'words.generated.adgangskode'}.",
|
"password_update_help": "Lad feltet være tomt @:{'words.generated.for'} at beholde den nuværende @:{'words.generated.adgangskode'}.",
|
||||||
@@ -5813,7 +6117,13 @@
|
|||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"edit": "@:{'words.generated.rediger'}",
|
"edit": "@:{'words.generated.rediger'}",
|
||||||
"deactivate": "Deaktiver"
|
"deactivate": "Deaktiver",
|
||||||
|
"login_link": "QR-login",
|
||||||
|
"login_link_title": "Medarbejderloginlink",
|
||||||
|
"login_link_qr_alt": "QR-kode-loginlink",
|
||||||
|
"login_link_url": "Loginlink",
|
||||||
|
"copy_login_link": "Kopiér link",
|
||||||
|
"login_link_copied": "Linket er kopieret."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -5843,7 +6153,132 @@
|
|||||||
"save_prices": "@.capitalize:{'words.generated.afdelingspriser'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.gemmes'}.",
|
"save_prices": "@.capitalize:{'words.generated.afdelingspriser'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.gemmes'}.",
|
||||||
"load_employees": "@.capitalize:{'words.generated.medarbejdere'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.indlæses'}.",
|
"load_employees": "@.capitalize:{'words.generated.medarbejdere'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.indlæses'}.",
|
||||||
"save_employee": "@.capitalize:{'words.generated.medarbejderen'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.gemmes'}.",
|
"save_employee": "@.capitalize:{'words.generated.medarbejderen'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.gemmes'}.",
|
||||||
"deactivate_employee": "@.capitalize:{'words.generated.medarbejderen'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} deaktiveres."
|
"deactivate_employee": "@.capitalize:{'words.generated.medarbejderen'} @:{'words.generated.kunne'} @:{'words.generated.ikke'} deaktiveres.",
|
||||||
|
"generate_employee_login_link": "Medarbejderens loginlink kunne ikke oprettes."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Rolletilladelser",
|
||||||
|
"tooltip": "Vis rolletilladelser",
|
||||||
|
"intro": "Roller samler de præcise funktioner, der tildeles en medarbejder, når rollen gemmes.",
|
||||||
|
"scope": "Alle funktioner er begrænset til de afdelinger, der vælges for medarbejderen.",
|
||||||
|
"selected": "Valgt",
|
||||||
|
"close": "Luk",
|
||||||
|
"unavailable": "Tilladelsesdetaljer er ikke tilgængelige for denne rolle, før rollekataloget er opdateret.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Konto",
|
||||||
|
"orders": "Ordrer",
|
||||||
|
"bookings": "Bookinger",
|
||||||
|
"time_bookings": "Tidsbookinger",
|
||||||
|
"reports": "Rapporter",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Øvrigt"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Log ind",
|
||||||
|
"description": "Giver medarbejderen adgang til at logge ind i Pleno."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "Se egne tilladelser",
|
||||||
|
"description": "Viser medarbejderen, hvilke adgangsrettigheder kontoen har."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "Se ordrer",
|
||||||
|
"description": "Kan åbne ordrelister og ordredetaljer for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Opret ordrer",
|
||||||
|
"description": "Kan oprette nye ordrer for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Rediger ordrer",
|
||||||
|
"description": "Kan ændre ordredetaljer for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Slet ordrer",
|
||||||
|
"description": "Kan slette ordrer i tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "Se ordrelinjer",
|
||||||
|
"description": "Kan se produkter, ydelser og priser på ordrer."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Opret ordrelinjer",
|
||||||
|
"description": "Kan tilføje produkter og ydelser til ordrer."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Rediger ordrelinjer",
|
||||||
|
"description": "Kan ændre produkter, antal og priser på ordrer."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Slet ordrelinjer",
|
||||||
|
"description": "Kan fjerne produkter og ydelser fra ordrer."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Opkræv ordrer",
|
||||||
|
"description": "Kan tage betaling eller opkræve en betalingsreservation for en ordre."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "Se afdelingsbookinger",
|
||||||
|
"description": "Kan se bookinger for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "Se egne bookinger",
|
||||||
|
"description": "Kan se bookingantal og bookinger knyttet til medarbejderkontoen."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Rediger bookinger",
|
||||||
|
"description": "Kan ændre tilladte bookingdetaljer."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Opret bookinger",
|
||||||
|
"description": "Kan oprette nye bookinger for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Afslut bookinger",
|
||||||
|
"description": "Kan markere bookinger som afsluttet."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Gensend bookingbekræftelser",
|
||||||
|
"description": "Kan gensende bookingbekræftelser og afslutningsmails."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "Se tidsbookinger",
|
||||||
|
"description": "Kan gennemse tidsbookingposter for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Opret tidsbookinger",
|
||||||
|
"description": "Kan oprette nye tidsbookingposter."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Rediger tidsbookinger",
|
||||||
|
"description": "Kan redigere tidsbookingposter, når backend-ruten understøtter det."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "Se ordrestatistik",
|
||||||
|
"description": "Kan åbne statistikvisninger for ordrer i tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "Se bookingstatistik",
|
||||||
|
"description": "Kan åbne statistikvisninger for bookinger i tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Åbn begrænset backoffice",
|
||||||
|
"description": "Kan tilgå det begrænsede backofficeområde."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Administrer afdelingspriser",
|
||||||
|
"description": "Kan redigere eksplicitte priser for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Administrer medarbejderadgang",
|
||||||
|
"description": "Kan oprette, redigere og deaktivere begrænsede backofficemedarbejdere."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Ekstra funktion",
|
||||||
|
"description": "Denne funktion kommer fra rollekataloget, men er endnu ikke beskrevet i grænsefladen."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1287,6 +1287,9 @@
|
|||||||
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
||||||
|
"autosave_saving": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
|
"autosave_pending": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
|
"autosave_saved": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
||||||
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
||||||
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
||||||
@@ -1301,6 +1304,15 @@
|
|||||||
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'templates.generated.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'templates.generated.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'templates.generated.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -1319,7 +1331,13 @@
|
|||||||
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'templates.generated.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'templates.generated.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -1349,7 +1367,132 @@
|
|||||||
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'templates.generated.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'templates.generated.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'templates.generated.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'templates.generated.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'templates.generated.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'templates.generated.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'templates.generated.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'templates.generated.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -1679,6 +1822,7 @@
|
|||||||
"company_phone": "Firmentelefon",
|
"company_phone": "Firmentelefon",
|
||||||
"contact_email": "@:{'words.generated.kontakt'}-@.upper:{'words.generated.e'}-@:{'words.generated.mail'}",
|
"contact_email": "@:{'words.generated.kontakt'}-@.upper:{'words.generated.e'}-@:{'words.generated.mail'}",
|
||||||
"contact_phone": "Kontakttelefonnummer",
|
"contact_phone": "Kontakttelefonnummer",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'words.generated.erstellt'} @:{'words.generated.von'}",
|
"created_by": "@.capitalize:{'words.generated.erstellt'} @:{'words.generated.von'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'words.generated.dieses'} @:{'words.generated.kennzeichen'} @:{'words.generated.gehoert'} @:{'words.generated.zu'} @:{'words.generated.einem'} @:{'words.generated.anderen'} @:{'words.generated.gespeicherten'} @:{'words.generated.kunden'}. @:{'words.generated.waehlen'} @.capitalize:{'words.generated.sie'}, @:{'words.generated.ob'} @:{'words.generated.der'} @:{'words.generated.ausgewaehlte'} @.capitalize:{'words.generated.kunde'} @:{'words.generated.beibehalten'} @:{'words.generated.oder'} @:{'words.generated.zum'} @:{'words.generated.fahrzeugkunden'} @:{'words.generated.gewechselt'} @:{'words.generated.werden'} @:{'words.generated.soll'}.",
|
"help_text": "@.capitalize:{'words.generated.dieses'} @:{'words.generated.kennzeichen'} @:{'words.generated.gehoert'} @:{'words.generated.zu'} @:{'words.generated.einem'} @:{'words.generated.anderen'} @:{'words.generated.gespeicherten'} @:{'words.generated.kunden'}. @:{'words.generated.waehlen'} @.capitalize:{'words.generated.sie'}, @:{'words.generated.ob'} @:{'words.generated.der'} @:{'words.generated.ausgewaehlte'} @.capitalize:{'words.generated.kunde'} @:{'words.generated.beibehalten'} @:{'words.generated.oder'} @:{'words.generated.zum'} @:{'words.generated.fahrzeugkunden'} @:{'words.generated.gewechselt'} @:{'words.generated.werden'} @:{'words.generated.soll'}.",
|
||||||
@@ -3227,6 +3371,7 @@
|
|||||||
"benefit_portal": "@:{'words.generated.kundenportal'}",
|
"benefit_portal": "@:{'words.generated.kundenportal'}",
|
||||||
"benefit_portal_desc": "@.capitalize:{'words.generated.nutzen'} @.capitalize:{'words.generated.sie'} @:{'words.generated.das'} @:{'words.generated.kundenportal'}, @:{'words.generated.um'} @:{'words.generated.fahrzeuge'} hinzuzuf?@:{'words.generated.gen'}, @.upper:{'words.generated.w'}?@:{'words.generated.schen'} @:{'words.generated.zu'} @:{'words.generated.buchen'}, @:{'words.generated.rechnungen'} @:{'words.generated.und'} @:{'words.generated.waschzertifikate'} herunterzuladen @:{'words.generated.sowie'} @:{'words.generated.informationen'} @:{'words.generated.zu'} @:{'words.generated.fr'}?@:{'words.generated.heren'} @.upper:{'words.generated.w'}?@:{'words.generated.schen'} einzusehen",
|
"benefit_portal_desc": "@.capitalize:{'words.generated.nutzen'} @.capitalize:{'words.generated.sie'} @:{'words.generated.das'} @:{'words.generated.kundenportal'}, @:{'words.generated.um'} @:{'words.generated.fahrzeuge'} hinzuzuf?@:{'words.generated.gen'}, @.upper:{'words.generated.w'}?@:{'words.generated.schen'} @:{'words.generated.zu'} @:{'words.generated.buchen'}, @:{'words.generated.rechnungen'} @:{'words.generated.und'} @:{'words.generated.waschzertifikate'} herunterzuladen @:{'words.generated.sowie'} @:{'words.generated.informationen'} @:{'words.generated.zu'} @:{'words.generated.fr'}?@:{'words.generated.heren'} @.upper:{'words.generated.w'}?@:{'words.generated.schen'} einzusehen",
|
||||||
"benefit_wash_desc": "@.capitalize:{'words.generated.nutzen'} @.capitalize:{'words.generated.sie'} @:{'words.generated.unser'} @:{'words.generated.nationales'} @:{'words.generated.netz'} @:{'words.generated.von'} @:{'words.generated.waschhallen'} @:{'words.generated.zu'} @:{'words.generated.wettbewerbsf'}?@:{'words.generated.higen'} @:{'words.generated.preisen'}",
|
"benefit_wash_desc": "@.capitalize:{'words.generated.nutzen'} @.capitalize:{'words.generated.sie'} @:{'words.generated.unser'} @:{'words.generated.nationales'} @:{'words.generated.netz'} @:{'words.generated.von'} @:{'words.generated.waschhallen'} @:{'words.generated.zu'} @:{'words.generated.wettbewerbsf'}?@:{'words.generated.higen'} @:{'words.generated.preisen'}",
|
||||||
|
"ean_label": "@:{'templates.generated.compat.admin.pos.ean'}",
|
||||||
"email_label": "Firmen-@.upper:{'words.generated.e'}-@:{'words.generated.mail'}",
|
"email_label": "Firmen-@.upper:{'words.generated.e'}-@:{'words.generated.mail'}",
|
||||||
"intro": "@.capitalize:{'words.generated.als'} @.capitalize:{'words.generated.kunde'} @:{'words.generated.bei'} @:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.erhalten'} @.capitalize:{'words.generated.sie'} @:{'words.generated.zugang'} @:{'words.generated.zu'}:",
|
"intro": "@.capitalize:{'words.generated.als'} @.capitalize:{'words.generated.kunde'} @:{'words.generated.bei'} @:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.erhalten'} @.capitalize:{'words.generated.sie'} @:{'words.generated.zugang'} @:{'words.generated.zu'}:",
|
||||||
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
||||||
@@ -3465,17 +3610,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Fehler melden",
|
"button": "Fehler melden",
|
||||||
"title": "Fehler melden",
|
"title": "Fehler melden",
|
||||||
"subtitle": "Sende den aktuellen Bildschirm und die letzten Fehlerdetails an den Support.",
|
"subtitle": "Sende die letzten Fehlerdetails an den Support. Ein Screenshot wird angehängt, wenn möglich.",
|
||||||
"before_error": "Was haben Sie getan, bevor der Fehler auftrat?",
|
"before_error": "Was haben Sie getan, bevor der Fehler auftrat?",
|
||||||
"expected": "Was hatten Sie erwartet?",
|
"expected": "Was hatten Sie erwartet?",
|
||||||
"actual": "Was ist tatsächlich passiert?",
|
"actual": "Was ist tatsächlich passiert?",
|
||||||
"before_error_placeholder": "Beschreiben Sie die Aktion, zum Beispiel Bestellungen öffnen oder einen Kunden auswählen.",
|
"before_error_placeholder": "Beschreiben Sie die Aktion, zum Beispiel Bestellungen öffnen oder einen Kunden auswählen.",
|
||||||
"expected_placeholder": "Beschreiben Sie das erwartete Ergebnis.",
|
"expected_placeholder": "Beschreiben Sie das erwartete Ergebnis.",
|
||||||
"actual_placeholder": "Beschreiben Sie, was stattdessen zu sehen war, inklusive Fehlermeldung.",
|
"actual_placeholder": "Beschreiben Sie, was stattdessen zu sehen war, inklusive Fehlermeldung.",
|
||||||
"consent": "Ich akzeptiere, dass der aktuelle App-Bildschirm, letzte Request-Fehler, Vue-Fehler, Browserdetails und meine Antworten zur Fehlersuche erfasst werden.",
|
"consent": "Ich akzeptiere, dass letzte Request-Fehler, Vue-Fehler, Browserdetails, meine Antworten und ein App-Screenshot, wenn möglich, zur Fehlersuche erfasst werden.",
|
||||||
"submit": "Bericht senden",
|
"submit": "Bericht senden",
|
||||||
"submitted": "Fehlerbericht gesendet.",
|
"submitted": "Fehlerbericht gesendet.",
|
||||||
"capture_failed": "Der Screenshot konnte nicht erstellt werden. Bitte versuchen Sie es erneut.",
|
"capture_failed": "Der Screenshot konnte nicht erstellt werden. Der Bericht wird ohne Screenshot gesendet.",
|
||||||
"submit_failed": "Der Fehlerbericht konnte nicht gesendet werden.",
|
"submit_failed": "Der Fehlerbericht konnte nicht gesendet werden.",
|
||||||
"required": "Alle Felder und die Zustimmung zur Datenerfassung sind erforderlich.",
|
"required": "Alle Felder und die Zustimmung zur Datenerfassung sind erforderlich.",
|
||||||
"page_subtitle": "Gemeldete Bildschirme, Request-Fehler und Vue-Fehler",
|
"page_subtitle": "Gemeldete Bildschirme, Request-Fehler und Vue-Fehler",
|
||||||
@@ -4015,6 +4160,15 @@
|
|||||||
"preview_title": "Preview @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
"preview_title": "Preview @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
||||||
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
||||||
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
||||||
|
},
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Zusammen abrechnen",
|
||||||
|
"split_by_month": "Nach Monat aufteilen",
|
||||||
|
"split_error_title": "Monatsaufteilung fehlgeschlagen",
|
||||||
|
"split_success_text": "{processed} Rechnungssammlungen verarbeitet. {changed} aufgeteilt, {skipped} übersprungen.",
|
||||||
|
"split_success_title": "Monatsaufteilung abgeschlossen",
|
||||||
|
"text": "Sie sind dabei, Aufträge aus mehreren Monaten gemeinsam abzurechnen ({months}). Sollen sie stattdessen nach Monat aufgeteilt werden?",
|
||||||
|
"title": "Aufträge aus mehreren Monaten"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoicing": {
|
"invoicing": {
|
||||||
@@ -4833,6 +4987,97 @@
|
|||||||
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.generated.database'} @:{'words.generated.and'} @:{'words.replication.services.redis'} @:{'words.generated.replication'}"
|
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.generated.database'} @:{'words.generated.and'} @:{'words.replication.services.redis'} @:{'words.generated.replication'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Zugriffsbereich",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Gruppe leeren",
|
||||||
|
"reload": "Neu laden",
|
||||||
|
"retry": "Erneut versuchen",
|
||||||
|
"select_all": "Gruppe auswaehlen"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Aktiviert",
|
||||||
|
"permission": "Berechtigung",
|
||||||
|
"route": "Route",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Erlaubt Zugriff auf {name}.",
|
||||||
|
"empty": "Es sind keine Berechtigungen verfuegbar.",
|
||||||
|
"empty_group": "Diese Gruppe hat keine Berechtigungen.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Rollenberechtigungen konnten nicht geladen werden.",
|
||||||
|
"update": "Eine oder mehrere Berechtigungen konnten nicht aktualisiert werden. Die aktuellen Rollenberechtigungen wurden neu geladen."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} Treffer",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Seitentyp- und Abteilungszugriff.",
|
||||||
|
"label": "Zugriff"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Berechtigungen fuer Buchungen, Zeitbuchungen und Planung.",
|
||||||
|
"label": "Buchungen"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Berechtigungen fuer Kunden, Benutzer, Mitarbeiter, Rollen und Anmeldung.",
|
||||||
|
"label": "Kunden und Benutzer"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Abteilungsbetrieb, Spuren, Relais, Benachrichtigungen, Berichte und Ziele.",
|
||||||
|
"label": "Abteilungen und Betrieb"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Berechtigungen fuer Rechnungen, Abrechnungsperioden, Inkasso und e-conomic.",
|
||||||
|
"label": "Rechnungen und e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Berechtigungen fuer Auftraege, POS, Zahlungen und Auftragszeilen.",
|
||||||
|
"label": "Auftraege und POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Berechtigungen, die keiner anderen Gruppe entsprechen.",
|
||||||
|
"label": "Sonstige"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Berechtigungen fuer Produkte, Preise, Rabatte und Waehrungen.",
|
||||||
|
"label": "Produkte und Preise"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Berechtigungen fuer Self-Service, Waschen, Scanner, Gateways und Edge-Hardware.",
|
||||||
|
"label": "Self-Service und Edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, Konfiguration, Release, Replikation und Integrationen.",
|
||||||
|
"label": "System und Konfiguration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "Keine Beschreibung verfuegbar.",
|
||||||
|
"no_search_results": "Keine Berechtigungen entsprechen der Suche.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Erlaubt Zugriff auf Admin-Dashboardseiten.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Erlaubt Zugriff auf Superuser-Dashboardseiten.",
|
||||||
|
"label": "Superuser"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Erlaubt Zugriff auf Benutzer-Dashboardseiten.",
|
||||||
|
"label": "Benutzer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Berechtigungen suchen",
|
||||||
|
"search_placeholder": "Nach Label, Schluessel, Beschreibung, Route oder Methode suchen",
|
||||||
|
"selected_summary": "{selected} von {total} ausgewaehlt",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Deaktiviert",
|
||||||
|
"enabled": "Aktiviert"
|
||||||
|
},
|
||||||
|
"subtitle": "Rollenberechtigungen",
|
||||||
|
"title": "Berechtigungen fuer Rolle {id}",
|
||||||
|
"title_with_name": "Berechtigungen fuer {name}"
|
||||||
|
},
|
||||||
"subtitle": "Benutzerrollen"
|
"subtitle": "Benutzerrollen"
|
||||||
},
|
},
|
||||||
"self_wash": {
|
"self_wash": {
|
||||||
@@ -5622,6 +5867,31 @@
|
|||||||
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
||||||
},
|
},
|
||||||
"other": "@.capitalize:{'words.generated.andere'}",
|
"other": "@.capitalize:{'words.generated.andere'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Aktive Fahrzeuge",
|
||||||
|
"attributes": "Attribute",
|
||||||
|
"connected": "Verbunden",
|
||||||
|
"customer_management": "Kundenverwaltung",
|
||||||
|
"customer_number_required": "Eine Kundennummer ist erforderlich, bevor Kundeneinstellungen verwaltet werden können.",
|
||||||
|
"customer_rules": "Kundenregeln",
|
||||||
|
"effective_access": "Effektiver Zugriff",
|
||||||
|
"loaded": "Geladen",
|
||||||
|
"load_failed": "Benutzerdaten konnten nicht geladen werden",
|
||||||
|
"loading_user": "Benutzer wird geladen",
|
||||||
|
"management_hub": "Verwaltung",
|
||||||
|
"missing": "Fehlt",
|
||||||
|
"more_permissions": "+{count} weitere Berechtigungen",
|
||||||
|
"no_subscription_transactions": "Für diesen Kunden sind keine Waschabonnement-Transaktionen registriert.",
|
||||||
|
"not_invoiced": "Nicht fakturiert",
|
||||||
|
"open_draft": "Offener Entwurf",
|
||||||
|
"open_vehicles": "Fahrzeuge öffnen",
|
||||||
|
"price_overrides": "Preisüberschreibungen",
|
||||||
|
"pricing": "Preise",
|
||||||
|
"subscription_invoicing": "Abonnementabrechnung",
|
||||||
|
"subtitle": "Überblick über Kundendaten, Zugriff, Preise, Fahrzeuge und Abonnementabrechnung.",
|
||||||
|
"vehicles_load_failed": "Fahrzeuge konnten nicht geladen werden.",
|
||||||
|
"workspace_shortcuts": "Arbeitsbereich"
|
||||||
|
},
|
||||||
"permissions": "Tilladelser",
|
"permissions": "Tilladelser",
|
||||||
"user_data": "Brugerdata",
|
"user_data": "Brugerdata",
|
||||||
"user_id": "@:{'words.generated.benutzer'}-@.upper:{'words.generated.id'}",
|
"user_id": "@:{'words.generated.benutzer'}-@.upper:{'words.generated.id'}",
|
||||||
@@ -5892,6 +6162,9 @@
|
|||||||
"title": "@.capitalize:{'words.generated.abteilungspreise'}",
|
"title": "@.capitalize:{'words.generated.abteilungspreise'}",
|
||||||
"save": "@:{'words.generated.preise'} @:{'words.generated.speichern'}",
|
"save": "@:{'words.generated.preise'} @:{'words.generated.speichern'}",
|
||||||
"saved": "@:{'words.generated.preise'} @:{'words.generated.gespeichert'}.",
|
"saved": "@:{'words.generated.preise'} @:{'words.generated.gespeichert'}.",
|
||||||
|
"autosave_saving": "Preise werden gespeichert...",
|
||||||
|
"autosave_pending": "Preisaenderungen ausstehend.",
|
||||||
|
"autosave_saved": "Alle Preisaenderungen gespeichert.",
|
||||||
"product": "@.capitalize:{'words.generated.produkt'}",
|
"product": "@.capitalize:{'words.generated.produkt'}",
|
||||||
"price": "Abteilungspreis",
|
"price": "Abteilungspreis",
|
||||||
"price_required": "Jedes @.capitalize:{'words.generated.produkt'} @:{'words.generated.muss'} einen @:{'words.generated.nicht'}-negativen numerischen Preis @:{'words.generated.haben'}.",
|
"price_required": "Jedes @.capitalize:{'words.generated.produkt'} @:{'words.generated.muss'} einen @:{'words.generated.nicht'}-negativen numerischen Preis @:{'words.generated.haben'}.",
|
||||||
@@ -5906,6 +6179,15 @@
|
|||||||
"edit_title": "@:{'words.generated.mitarbeiter'} @:{'words.generated.bearbeiten'}",
|
"edit_title": "@:{'words.generated.mitarbeiter'} @:{'words.generated.bearbeiten'}",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
|
"user_id": "Benutzer-ID",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"phone_validation": "Die Telefonnummer muss 4-15 Ziffern enthalten.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Dänemark",
|
||||||
|
"sweden": "Schweden",
|
||||||
|
"norway": "Norwegen",
|
||||||
|
"finland": "Finnland"
|
||||||
|
},
|
||||||
"password": "@:{'words.generated.passwort'}",
|
"password": "@:{'words.generated.passwort'}",
|
||||||
"password_create_help": "@.capitalize:{'words.generated.fur'} neue @:{'words.generated.mitarbeiter'} @:{'words.generated.erforderlich'}. Verwenden @.capitalize:{'words.generated.sie'} @:{'words.generated.mindestens'} 8 Zeichen.",
|
"password_create_help": "@.capitalize:{'words.generated.fur'} neue @:{'words.generated.mitarbeiter'} @:{'words.generated.erforderlich'}. Verwenden @.capitalize:{'words.generated.sie'} @:{'words.generated.mindestens'} 8 Zeichen.",
|
||||||
"password_update_help": "Leer lassen, um das vorhandene @:{'words.generated.passwort'} beizubehalten.",
|
"password_update_help": "Leer lassen, um das vorhandene @:{'words.generated.passwort'} beizubehalten.",
|
||||||
@@ -5924,7 +6206,13 @@
|
|||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"edit": "@.capitalize:{'words.generated.bearbeiten'}",
|
"edit": "@.capitalize:{'words.generated.bearbeiten'}",
|
||||||
"deactivate": "Deaktivieren"
|
"deactivate": "Deaktivieren",
|
||||||
|
"login_link": "QR-Login",
|
||||||
|
"login_link_title": "Mitarbeiter-Loginlink",
|
||||||
|
"login_link_qr_alt": "QR-Code-Loginlink",
|
||||||
|
"login_link_url": "Loginlink",
|
||||||
|
"copy_login_link": "Link kopieren",
|
||||||
|
"login_link_copied": "Link kopiert."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -5954,7 +6242,132 @@
|
|||||||
"save_prices": "@.capitalize:{'words.generated.abteilungspreise'} @:{'words.generated.konnten'} @:{'words.generated.nicht'} @:{'words.generated.gespeichert'} @:{'words.generated.werden'}.",
|
"save_prices": "@.capitalize:{'words.generated.abteilungspreise'} @:{'words.generated.konnten'} @:{'words.generated.nicht'} @:{'words.generated.gespeichert'} @:{'words.generated.werden'}.",
|
||||||
"load_employees": "@:{'words.generated.mitarbeiter'} @:{'words.generated.konnten'} @:{'words.generated.nicht'} @:{'words.generated.geladen'} @:{'words.generated.werden'}.",
|
"load_employees": "@:{'words.generated.mitarbeiter'} @:{'words.generated.konnten'} @:{'words.generated.nicht'} @:{'words.generated.geladen'} @:{'words.generated.werden'}.",
|
||||||
"save_employee": "@:{'words.generated.mitarbeiter'} @:{'words.generated.konnte'} @:{'words.generated.nicht'} @:{'words.generated.gespeichert'} @:{'words.generated.werden'}.",
|
"save_employee": "@:{'words.generated.mitarbeiter'} @:{'words.generated.konnte'} @:{'words.generated.nicht'} @:{'words.generated.gespeichert'} @:{'words.generated.werden'}.",
|
||||||
"deactivate_employee": "@:{'words.generated.mitarbeiter'} @:{'words.generated.konnte'} @:{'words.generated.nicht'} @:{'words.generated.deaktiviert'} @:{'words.generated.werden'}."
|
"deactivate_employee": "@:{'words.generated.mitarbeiter'} @:{'words.generated.konnte'} @:{'words.generated.nicht'} @:{'words.generated.deaktiviert'} @:{'words.generated.werden'}.",
|
||||||
|
"generate_employee_login_link": "Mitarbeiter-Loginlink konnte nicht erstellt werden."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Rollenberechtigungen",
|
||||||
|
"tooltip": "Rollenberechtigungen anzeigen",
|
||||||
|
"intro": "Rollen bündeln die genauen Funktionen, die einem Mitarbeiter beim Speichern der Rolle zugewiesen werden.",
|
||||||
|
"scope": "Jede Funktion ist auf die Abteilungen begrenzt, die für den Mitarbeiter ausgewählt sind.",
|
||||||
|
"selected": "Ausgewählt",
|
||||||
|
"close": "Schließen",
|
||||||
|
"unavailable": "Berechtigungsdetails sind für diese Rolle erst verfügbar, wenn der Rollenkatalog aktualisiert wurde.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Konto",
|
||||||
|
"orders": "Aufträge",
|
||||||
|
"bookings": "Buchungen",
|
||||||
|
"time_bookings": "Zeitbuchungen",
|
||||||
|
"reports": "Berichte",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Sonstiges"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Anmelden",
|
||||||
|
"description": "Erlaubt dem Mitarbeiter, sich bei Pleno anzumelden."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "Eigene Berechtigungen anzeigen",
|
||||||
|
"description": "Zeigt dem Mitarbeiter, welche Zugriffsrechte das Konto hat."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "Aufträge anzeigen",
|
||||||
|
"description": "Kann Auftragslisten und Auftragsdetails für zugewiesene Abteilungen öffnen."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Aufträge erstellen",
|
||||||
|
"description": "Kann neue Aufträge für zugewiesene Abteilungen erstellen."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Aufträge bearbeiten",
|
||||||
|
"description": "Kann Auftragsdetails für zugewiesene Abteilungen ändern."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Aufträge löschen",
|
||||||
|
"description": "Kann Aufträge in zugewiesenen Abteilungen löschen."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "Auftragspositionen anzeigen",
|
||||||
|
"description": "Kann Produkte, Leistungen und Preise auf Aufträgen sehen."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Auftragspositionen erstellen",
|
||||||
|
"description": "Kann Produkte und Leistungen zu Aufträgen hinzufügen."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Auftragspositionen bearbeiten",
|
||||||
|
"description": "Kann Produkte, Mengen und Preise auf Aufträgen ändern."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Auftragspositionen löschen",
|
||||||
|
"description": "Kann Produkte und Leistungen aus Aufträgen entfernen."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Aufträge abrechnen",
|
||||||
|
"description": "Kann Zahlungen annehmen oder eine Zahlungsreservierung für einen Auftrag belasten."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "Abteilungsbuchungen anzeigen",
|
||||||
|
"description": "Kann Buchungen für zugewiesene Abteilungen sehen."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "Eigene Buchungen anzeigen",
|
||||||
|
"description": "Kann Buchungszahlen und Buchungen sehen, die mit dem Mitarbeiterkonto verbunden sind."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Buchungen bearbeiten",
|
||||||
|
"description": "Kann erlaubte Buchungsdetails ändern."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Buchungen erstellen",
|
||||||
|
"description": "Kann neue Buchungen für zugewiesene Abteilungen erstellen."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Buchungen abschließen",
|
||||||
|
"description": "Kann Buchungen als abgeschlossen markieren."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Buchungsbestätigungen erneut senden",
|
||||||
|
"description": "Kann Buchungsbestätigungen und Abschluss-E-Mails erneut senden."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "Zeitbuchungen anzeigen",
|
||||||
|
"description": "Kann Zeitbuchungseinträge für zugewiesene Abteilungen prüfen."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Zeitbuchungen erstellen",
|
||||||
|
"description": "Kann neue Zeitbuchungseinträge hinzufügen."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Zeitbuchungen bearbeiten",
|
||||||
|
"description": "Kann Zeitbuchungseinträge bearbeiten, wenn die Backend-Route dies unterstützt."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "Auftragsstatistik anzeigen",
|
||||||
|
"description": "Kann Auftragsstatistiken für zugewiesene Abteilungen öffnen."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "Buchungsstatistik anzeigen",
|
||||||
|
"description": "Kann Buchungsstatistiken für zugewiesene Abteilungen öffnen."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Begrenztes Backoffice öffnen",
|
||||||
|
"description": "Kann den begrenzten Backofficebereich öffnen."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Abteilungspreise verwalten",
|
||||||
|
"description": "Kann explizite Preise für zugewiesene Abteilungen bearbeiten."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Mitarbeiterzugang verwalten",
|
||||||
|
"description": "Kann begrenzte Backoffice-Mitarbeiter erstellen, bearbeiten und deaktivieren."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Zusätzliche Funktion",
|
||||||
|
"description": "Diese Funktion kommt aus dem Rollenkatalog, ist aber in der Oberfläche noch nicht beschrieben."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1008,6 +1008,9 @@
|
|||||||
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
||||||
|
"autosave_saving": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
|
"autosave_pending": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
|
"autosave_saved": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
||||||
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
||||||
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
||||||
@@ -1022,6 +1025,15 @@
|
|||||||
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'templates.generated.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'templates.generated.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'templates.generated.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -1040,7 +1052,13 @@
|
|||||||
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'templates.generated.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'templates.generated.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -1070,7 +1088,132 @@
|
|||||||
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'templates.generated.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'templates.generated.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'templates.generated.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'templates.generated.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'templates.generated.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'templates.generated.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'templates.generated.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'templates.generated.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -1400,6 +1543,7 @@
|
|||||||
"company_phone": "@.capitalize:{'words.generated.company'} @:{'words.generated.phone'}",
|
"company_phone": "@.capitalize:{'words.generated.company'} @:{'words.generated.phone'}",
|
||||||
"contact_email": "@.capitalize:{'words.generated.contact'} @:{'words.generated.email'}",
|
"contact_email": "@.capitalize:{'words.generated.contact'} @:{'words.generated.email'}",
|
||||||
"contact_phone": "@.capitalize:{'words.generated.contact'} @:{'words.generated.phone'}",
|
"contact_phone": "@.capitalize:{'words.generated.contact'} @:{'words.generated.phone'}",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'words.generated.created'} @:{'words.generated.by'}",
|
"created_by": "@.capitalize:{'words.generated.created'} @:{'words.generated.by'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'words.generated.this'} @:{'words.generated.typed'} @:{'words.generated.plate'} @:{'words.generated.belongs'} @:{'words.generated.to'} @:{'words.generated.another'} @:{'words.generated.saved'} @:{'words.generated.customer'}. @.capitalize:{'words.generated.choose'} @:{'words.generated.whether'} @:{'words.generated.to'} @:{'words.generated.keep'} @:{'words.replication.article.host_mention'} @:{'words.generated.selected'} @:{'words.generated.customer'} @:{'words.generated.or'} @:{'words.generated.switch'} @:{'words.generated.to'} @:{'words.replication.article.host_mention'} @:{'words.generated.vehicle'} @:{'words.generated.customer'}.",
|
"help_text": "@.capitalize:{'words.generated.this'} @:{'words.generated.typed'} @:{'words.generated.plate'} @:{'words.generated.belongs'} @:{'words.generated.to'} @:{'words.generated.another'} @:{'words.generated.saved'} @:{'words.generated.customer'}. @.capitalize:{'words.generated.choose'} @:{'words.generated.whether'} @:{'words.generated.to'} @:{'words.generated.keep'} @:{'words.replication.article.host_mention'} @:{'words.generated.selected'} @:{'words.generated.customer'} @:{'words.generated.or'} @:{'words.generated.switch'} @:{'words.generated.to'} @:{'words.replication.article.host_mention'} @:{'words.generated.vehicle'} @:{'words.generated.customer'}.",
|
||||||
@@ -2948,6 +3092,7 @@
|
|||||||
"benefit_portal": "@.capitalize:{'words.generated.customer'} @.capitalize:{'words.generated.portal'}",
|
"benefit_portal": "@.capitalize:{'words.generated.customer'} @.capitalize:{'words.generated.portal'}",
|
||||||
"benefit_portal_desc": "@.capitalize:{'words.generated.use'} @:{'words.replication.article.host_mention'} @:{'words.generated.customer'} @:{'words.generated.portal'} @:{'words.generated.to'} @:{'words.generated.add'} @:{'words.generated.vehicles'}, @:{'words.generated.book'} @:{'words.generated.washes'}, @:{'words.generated.download'} @:{'words.generated.invoices'}, @:{'words.generated.wash'} @:{'words.generated.certificates'} @:{'words.generated.and'} @:{'words.generated.information'} @:{'words.generated.for'} @:{'words.generated.historical'} @:{'words.generated.washes'}",
|
"benefit_portal_desc": "@.capitalize:{'words.generated.use'} @:{'words.replication.article.host_mention'} @:{'words.generated.customer'} @:{'words.generated.portal'} @:{'words.generated.to'} @:{'words.generated.add'} @:{'words.generated.vehicles'}, @:{'words.generated.book'} @:{'words.generated.washes'}, @:{'words.generated.download'} @:{'words.generated.invoices'}, @:{'words.generated.wash'} @:{'words.generated.certificates'} @:{'words.generated.and'} @:{'words.generated.information'} @:{'words.generated.for'} @:{'words.generated.historical'} @:{'words.generated.washes'}",
|
||||||
"benefit_wash_desc": "@.capitalize:{'words.generated.use'} @:{'words.generated.our'} @:{'words.generated.national'} @:{'words.generated.network'} @:{'words.generated.of'} @:{'words.generated.wash'} @:{'words.generated.halls'} @:{'words.generated.at'} @:{'words.generated.competitive'} @:{'words.generated.prices'}",
|
"benefit_wash_desc": "@.capitalize:{'words.generated.use'} @:{'words.generated.our'} @:{'words.generated.national'} @:{'words.generated.network'} @:{'words.generated.of'} @:{'words.generated.wash'} @:{'words.generated.halls'} @:{'words.generated.at'} @:{'words.generated.competitive'} @:{'words.generated.prices'}",
|
||||||
|
"ean_label": "@:{'templates.generated.compat.admin.pos.ean'}",
|
||||||
"email_label": "@.capitalize:{'words.generated.company'} @:{'words.generated.email'}",
|
"email_label": "@.capitalize:{'words.generated.company'} @:{'words.generated.email'}",
|
||||||
"intro": "@.capitalize:{'words.generated.as'} @:{'words.generated.a'} @:{'words.generated.customer'} @:{'words.generated.at'} @.capitalize:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.you'} @:{'words.generated.get'} @:{'words.generated.access'} @:{'words.generated.to'}:",
|
"intro": "@.capitalize:{'words.generated.as'} @:{'words.generated.a'} @:{'words.generated.customer'} @:{'words.generated.at'} @.capitalize:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.you'} @:{'words.generated.get'} @:{'words.generated.access'} @:{'words.generated.to'}:",
|
||||||
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
||||||
@@ -3163,6 +3308,13 @@
|
|||||||
"opening_hours": {
|
"opening_hours": {
|
||||||
"closed": "@:{'templates.generated.compat.global.closed'}"
|
"closed": "@:{'templates.generated.compat.global.closed'}"
|
||||||
},
|
},
|
||||||
|
"pricing": {
|
||||||
|
"custom_pricing_disabled": "Fallback enabled",
|
||||||
|
"custom_pricing_enabled": "Custom only",
|
||||||
|
"custom_pricing_missing_price": "Missing department prices resolve to 999999.",
|
||||||
|
"custom_pricing_only": "No fallback pricing",
|
||||||
|
"effective_department_price": "Effective department price"
|
||||||
|
},
|
||||||
"search_placeholder": "@.capitalize:{'words.generated.search'} @:{'words.generated.by'} @:{'words.generated.department'} @:{'words.generated.name'}",
|
"search_placeholder": "@.capitalize:{'words.generated.search'} @:{'words.generated.by'} @:{'words.generated.department'} @:{'words.generated.name'}",
|
||||||
"select_department": "@.capitalize:{'words.generated.select'} @:{'words.generated.a'} @:{'words.generated.department'}",
|
"select_department": "@.capitalize:{'words.generated.select'} @:{'words.generated.a'} @:{'words.generated.department'}",
|
||||||
"subtitle": "@.capitalize:{'words.generated.manage'} @:{'words.generated.departments'}",
|
"subtitle": "@.capitalize:{'words.generated.manage'} @:{'words.generated.departments'}",
|
||||||
@@ -3186,17 +3338,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Report error",
|
"button": "Report error",
|
||||||
"title": "Report error",
|
"title": "Report error",
|
||||||
"subtitle": "Send the current screen and recent error details to support.",
|
"subtitle": "Send recent error details to support. A screenshot is attached when available.",
|
||||||
"before_error": "What were you doing before the error occurred?",
|
"before_error": "What were you doing before the error occurred?",
|
||||||
"expected": "What did you expect would happen?",
|
"expected": "What did you expect would happen?",
|
||||||
"actual": "What actually happened?",
|
"actual": "What actually happened?",
|
||||||
"before_error_placeholder": "Describe the action you were taking, for example opening orders or selecting a customer.",
|
"before_error_placeholder": "Describe the action you were taking, for example opening orders or selecting a customer.",
|
||||||
"expected_placeholder": "Describe the result you expected to see.",
|
"expected_placeholder": "Describe the result you expected to see.",
|
||||||
"actual_placeholder": "Describe what you saw instead, including any error text.",
|
"actual_placeholder": "Describe what you saw instead, including any error text.",
|
||||||
"consent": "I accept that the current app screen, recent request errors, Vue errors, browser details, and my answers are collected for troubleshooting.",
|
"consent": "I accept that recent request errors, Vue errors, browser details, my answers, and an app screenshot when available are collected for troubleshooting.",
|
||||||
"submit": "Submit report",
|
"submit": "Submit report",
|
||||||
"submitted": "Error report submitted.",
|
"submitted": "Error report submitted.",
|
||||||
"capture_failed": "The screen capture failed. Please try again.",
|
"capture_failed": "The screen capture failed. The report will be sent without a screenshot.",
|
||||||
"submit_failed": "The error report could not be submitted.",
|
"submit_failed": "The error report could not be submitted.",
|
||||||
"required": "All fields and data collection acceptance are required.",
|
"required": "All fields and data collection acceptance are required.",
|
||||||
"page_subtitle": "Reported screens, request failures, and Vue errors",
|
"page_subtitle": "Reported screens, request failures, and Vue errors",
|
||||||
@@ -3736,6 +3888,15 @@
|
|||||||
"preview_title": "@.capitalize:{'words.generated.preview'} @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
"preview_title": "@.capitalize:{'words.generated.preview'} @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
||||||
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
||||||
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
||||||
|
},
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Invoice together",
|
||||||
|
"split_by_month": "Split by month",
|
||||||
|
"split_error_title": "Monthly split failed",
|
||||||
|
"split_success_text": "Processed {processed} invoice collections. Split {changed}, skipped {skipped}.",
|
||||||
|
"split_success_title": "Monthly split completed",
|
||||||
|
"text": "You are about to invoice orders from multiple months together ({months}). Should they be split by month instead?",
|
||||||
|
"title": "Orders from multiple months"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoicing": {
|
"invoicing": {
|
||||||
@@ -3993,6 +4154,7 @@
|
|||||||
"departments": {
|
"departments": {
|
||||||
"columns": {
|
"columns": {
|
||||||
"archived": "@:{'words.generated.archived'}",
|
"archived": "@:{'words.generated.archived'}",
|
||||||
|
"custom_pricing_only": "No fallback pricing",
|
||||||
"dimension": "Dimension",
|
"dimension": "Dimension",
|
||||||
"economic_department": "@.upper:{'words.generated.e'}-@:{'words.generated.conomic'} @.capitalize:{'words.generated.department'}",
|
"economic_department": "@.upper:{'words.generated.e'}-@:{'words.generated.conomic'} @.capitalize:{'words.generated.department'}",
|
||||||
"latitude": "@:{'templates.generated.compat.departments.form.latitude'}",
|
"latitude": "@:{'templates.generated.compat.departments.form.latitude'}",
|
||||||
@@ -4554,6 +4716,97 @@
|
|||||||
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.replication.services.database'}, @:{'words.replication.services.redis'}, @:{'words.generated.and'} @:{'words.replication.services.minio'} @:{'words.generated.replication'}"
|
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.replication.services.database'}, @:{'words.replication.services.redis'}, @:{'words.generated.and'} @:{'words.replication.services.minio'} @:{'words.generated.replication'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Access scope",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Clear group",
|
||||||
|
"reload": "Reload",
|
||||||
|
"retry": "Try again",
|
||||||
|
"select_all": "Select group"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"permission": "Permission",
|
||||||
|
"route": "Route",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Allows access to {name}.",
|
||||||
|
"empty": "No permissions are available.",
|
||||||
|
"empty_group": "This group has no permissions.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Could not load role permissions.",
|
||||||
|
"update": "Could not update one or more permissions. The current role permissions were reloaded."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} matching",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Page type and department access permissions.",
|
||||||
|
"label": "Access"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Booking, time booking, and planning permissions.",
|
||||||
|
"label": "Bookings"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Customer, user, employee, role, and authentication permissions.",
|
||||||
|
"label": "Customers and users"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Department operations, lanes, relays, notifications, reports, and goals.",
|
||||||
|
"label": "Departments and operations"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Invoice, billing period, collection, and e-conomic permissions.",
|
||||||
|
"label": "Invoicing and e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Order, POS, payment, and order line permissions.",
|
||||||
|
"label": "Orders and POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Permissions that do not match another group.",
|
||||||
|
"label": "Other"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Product, price, discount, and currency permissions.",
|
||||||
|
"label": "Products and pricing"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Self-serve, wash, scanner, gateway, and edge hardware permissions.",
|
||||||
|
"label": "Self-serve and edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, configuration, release, replication, and integration permissions.",
|
||||||
|
"label": "System and configuration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "No description is available.",
|
||||||
|
"no_search_results": "No permissions match the current search.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Allows access to admin dashboard pages.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Allows access to superuser dashboard pages.",
|
||||||
|
"label": "Superuser"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Allows access to user dashboard pages.",
|
||||||
|
"label": "User"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Search permissions",
|
||||||
|
"search_placeholder": "Search by label, key, description, route, or method",
|
||||||
|
"selected_summary": "{selected} of {total} selected",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"enabled": "Enabled"
|
||||||
|
},
|
||||||
|
"subtitle": "Role permissions",
|
||||||
|
"title": "Role {id} permissions",
|
||||||
|
"title_with_name": "{name} permissions"
|
||||||
|
},
|
||||||
"subtitle": "@.capitalize:{'words.generated.user'} @:{'words.generated.roles'}"
|
"subtitle": "@.capitalize:{'words.generated.user'} @:{'words.generated.roles'}"
|
||||||
},
|
},
|
||||||
"self_wash": {
|
"self_wash": {
|
||||||
@@ -5343,6 +5596,31 @@
|
|||||||
"transaction_history": "@.capitalize:{'words.generated.transaction'} @:{'words.generated.history'}"
|
"transaction_history": "@.capitalize:{'words.generated.transaction'} @:{'words.generated.history'}"
|
||||||
},
|
},
|
||||||
"other": "@:{'templates.generated.compat.global.other'}",
|
"other": "@:{'templates.generated.compat.global.other'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Active vehicles",
|
||||||
|
"attributes": "Attributes",
|
||||||
|
"connected": "Connected",
|
||||||
|
"customer_management": "Customer management",
|
||||||
|
"customer_number_required": "A customer number is required before customer settings can be managed.",
|
||||||
|
"customer_rules": "Customer rules",
|
||||||
|
"effective_access": "Effective access",
|
||||||
|
"loaded": "Loaded",
|
||||||
|
"load_failed": "User data could not be loaded",
|
||||||
|
"loading_user": "Loading user",
|
||||||
|
"management_hub": "Management hub",
|
||||||
|
"missing": "Missing",
|
||||||
|
"more_permissions": "+{count} more permissions",
|
||||||
|
"no_subscription_transactions": "No wash subscription transactions are registered for this customer.",
|
||||||
|
"not_invoiced": "Not invoiced",
|
||||||
|
"open_draft": "Open draft",
|
||||||
|
"open_vehicles": "Open vehicles",
|
||||||
|
"price_overrides": "Price overrides",
|
||||||
|
"pricing": "Pricing",
|
||||||
|
"subscription_invoicing": "Subscription invoicing",
|
||||||
|
"subtitle": "Overview of customer data, access, pricing, vehicles, and subscription invoicing.",
|
||||||
|
"vehicles_load_failed": "Vehicles could not be loaded.",
|
||||||
|
"workspace_shortcuts": "Workspace shortcuts"
|
||||||
|
},
|
||||||
"permissions": "@.capitalize:{'words.generated.permissions'}",
|
"permissions": "@.capitalize:{'words.generated.permissions'}",
|
||||||
"user_data": "@.capitalize:{'words.generated.user'} @:{'words.generated.data'}",
|
"user_data": "@.capitalize:{'words.generated.user'} @:{'words.generated.data'}",
|
||||||
"user_id": "@.capitalize:{'words.generated.user'} @.upper:{'words.generated.id'}",
|
"user_id": "@.capitalize:{'words.generated.user'} @.upper:{'words.generated.id'}",
|
||||||
@@ -5549,6 +5827,20 @@
|
|||||||
},
|
},
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"add": "@.capitalize:{'words.generated.add'} @:{'words.generated.vehicle'}",
|
"add": "@.capitalize:{'words.generated.add'} @:{'words.generated.vehicle'}",
|
||||||
|
"add_modal": {
|
||||||
|
"customer_label": "Customer",
|
||||||
|
"customer_placeholder": "Search by customer name or number",
|
||||||
|
"error": "Unable to add vehicle.",
|
||||||
|
"no_customer_results": "No customers found",
|
||||||
|
"reference_placeholder": "Optional reference",
|
||||||
|
"registration_placeholder": "Registration number",
|
||||||
|
"selected_customer": "Selected customer",
|
||||||
|
"submit": "Add vehicle",
|
||||||
|
"title": "Add vehicle",
|
||||||
|
"type_load_error": "Unable to load vehicle types.",
|
||||||
|
"type_placeholder": "Select vehicle type",
|
||||||
|
"validation_error": "Select a customer, registration number, and vehicle type."
|
||||||
|
},
|
||||||
"brand": "Brand",
|
"brand": "Brand",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"delete_vehicle": "@:{'templates.generated.compat.vehicles.delete'}",
|
"delete_vehicle": "@:{'templates.generated.compat.vehicles.delete'}",
|
||||||
@@ -5613,6 +5905,9 @@
|
|||||||
"title": "@.capitalize:{'words.generated.department'} @:{'words.generated.prices'}",
|
"title": "@.capitalize:{'words.generated.department'} @:{'words.generated.prices'}",
|
||||||
"save": "@.capitalize:{'words.generated.save'} @:{'words.generated.prices'}",
|
"save": "@.capitalize:{'words.generated.save'} @:{'words.generated.prices'}",
|
||||||
"saved": "@.capitalize:{'words.generated.prices'} @:{'words.generated.saved'}.",
|
"saved": "@.capitalize:{'words.generated.prices'} @:{'words.generated.saved'}.",
|
||||||
|
"autosave_saving": "Saving prices...",
|
||||||
|
"autosave_pending": "Price changes pending.",
|
||||||
|
"autosave_saved": "All price changes saved.",
|
||||||
"product": "@.capitalize:{'words.generated.product'}",
|
"product": "@.capitalize:{'words.generated.product'}",
|
||||||
"price": "@.capitalize:{'words.generated.department'} @:{'words.generated.price'}",
|
"price": "@.capitalize:{'words.generated.department'} @:{'words.generated.price'}",
|
||||||
"price_required": "Every @:{'words.generated.product'} @:{'words.generated.must'} @:{'words.generated.have'} @:{'words.generated.a'} non-negative numeric @:{'words.generated.price'}.",
|
"price_required": "Every @:{'words.generated.product'} @:{'words.generated.must'} @:{'words.generated.have'} @:{'words.generated.a'} non-negative numeric @:{'words.generated.price'}.",
|
||||||
@@ -5627,6 +5922,15 @@
|
|||||||
"edit_title": "@.capitalize:{'words.generated.edit'} @:{'words.generated.employee'}",
|
"edit_title": "@.capitalize:{'words.generated.edit'} @:{'words.generated.employee'}",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"user_id": "User ID",
|
||||||
|
"phone": "Phone",
|
||||||
|
"phone_validation": "Phone number must be 4-15 digits.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Denmark",
|
||||||
|
"sweden": "Sweden",
|
||||||
|
"norway": "Norway",
|
||||||
|
"finland": "Finland"
|
||||||
|
},
|
||||||
"password": "@.capitalize:{'words.generated.password'}",
|
"password": "@.capitalize:{'words.generated.password'}",
|
||||||
"password_create_help": "@.capitalize:{'words.generated.required'} @:{'words.generated.for'} new @:{'words.generated.employees'}. Use @:{'words.generated.at'} @:{'words.generated.least'} 8 characters.",
|
"password_create_help": "@.capitalize:{'words.generated.required'} @:{'words.generated.for'} new @:{'words.generated.employees'}. Use @:{'words.generated.at'} @:{'words.generated.least'} 8 characters.",
|
||||||
"password_update_help": "Leave blank @:{'words.generated.to'} keep @:{'words.replication.article.host_mention'} existing @:{'words.generated.password'}.",
|
"password_update_help": "Leave blank @:{'words.generated.to'} keep @:{'words.replication.article.host_mention'} existing @:{'words.generated.password'}.",
|
||||||
@@ -5645,7 +5949,13 @@
|
|||||||
"active": "Active",
|
"active": "Active",
|
||||||
"inactive": "@.capitalize:{'words.generated.inactive'}",
|
"inactive": "@.capitalize:{'words.generated.inactive'}",
|
||||||
"edit": "@.capitalize:{'words.generated.edit'}",
|
"edit": "@.capitalize:{'words.generated.edit'}",
|
||||||
"deactivate": "@.capitalize:{'words.generated.deactivate'}"
|
"deactivate": "@.capitalize:{'words.generated.deactivate'}",
|
||||||
|
"login_link": "QR login",
|
||||||
|
"login_link_title": "Employee login link",
|
||||||
|
"login_link_qr_alt": "QR code login link",
|
||||||
|
"login_link_url": "Login link",
|
||||||
|
"copy_login_link": "Copy link",
|
||||||
|
"login_link_copied": "Link copied."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -5675,7 +5985,132 @@
|
|||||||
"save_prices": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.save'} @:{'words.generated.department'} @:{'words.generated.prices'}.",
|
"save_prices": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.save'} @:{'words.generated.department'} @:{'words.generated.prices'}.",
|
||||||
"load_employees": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.load'} @:{'words.generated.employees'}.",
|
"load_employees": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.load'} @:{'words.generated.employees'}.",
|
||||||
"save_employee": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.save'} @:{'words.generated.employee'}.",
|
"save_employee": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.save'} @:{'words.generated.employee'}.",
|
||||||
"deactivate_employee": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.deactivate'} @:{'words.generated.employee'}."
|
"deactivate_employee": "@.capitalize:{'words.generated.could'} @:{'words.generated.not'} @:{'words.generated.deactivate'} @:{'words.generated.employee'}.",
|
||||||
|
"generate_employee_login_link": "Could not create employee login link."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Role permissions",
|
||||||
|
"tooltip": "View role permissions",
|
||||||
|
"intro": "Roles combine the exact capabilities assigned to an employee when the role is saved.",
|
||||||
|
"scope": "Every capability is limited to the departments selected for the employee.",
|
||||||
|
"selected": "Selected",
|
||||||
|
"close": "Close",
|
||||||
|
"unavailable": "Permission details are unavailable for this role until the role catalog has been updated.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Account",
|
||||||
|
"orders": "Orders",
|
||||||
|
"bookings": "Bookings",
|
||||||
|
"time_bookings": "Time bookings",
|
||||||
|
"reports": "Reports",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Other"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Sign in",
|
||||||
|
"description": "Allows the employee to sign in to Pleno."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "View own permissions",
|
||||||
|
"description": "Shows the employee which access rights their account has."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "View orders",
|
||||||
|
"description": "Can open order lists and order details for assigned departments."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Create orders",
|
||||||
|
"description": "Can create new orders for assigned departments."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Edit orders",
|
||||||
|
"description": "Can update order details for assigned departments."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Delete orders",
|
||||||
|
"description": "Can delete orders in assigned departments."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "View order lines",
|
||||||
|
"description": "Can see products, services, and prices on orders."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Create order lines",
|
||||||
|
"description": "Can add products and services to orders."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Edit order lines",
|
||||||
|
"description": "Can change products, quantities, and prices on orders."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Delete order lines",
|
||||||
|
"description": "Can remove products and services from orders."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Charge orders",
|
||||||
|
"description": "Can take payment or charge a payment intent for an order."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "View department bookings",
|
||||||
|
"description": "Can see bookings for assigned departments."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "View own bookings",
|
||||||
|
"description": "Can see booking counts and bookings tied to the employee account."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Edit bookings",
|
||||||
|
"description": "Can change permitted booking details."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Create bookings",
|
||||||
|
"description": "Can create new bookings for assigned departments."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Complete bookings",
|
||||||
|
"description": "Can mark bookings as completed."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Resend booking confirmations",
|
||||||
|
"description": "Can resend booking confirmation and completion emails."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "View time booking entries",
|
||||||
|
"description": "Can inspect time-booking entries for assigned departments."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Create time booking entries",
|
||||||
|
"description": "Can add new time-booking entries."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Edit time booking entries",
|
||||||
|
"description": "Can edit time-booking entries when the backend route supports it."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "View order statistics",
|
||||||
|
"description": "Can open order statistics views for assigned departments."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "View booking statistics",
|
||||||
|
"description": "Can open booking statistics views for assigned departments."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Open limited backoffice",
|
||||||
|
"description": "Can access the limited backoffice area."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Manage department prices",
|
||||||
|
"description": "Can edit explicit prices for assigned departments."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Manage employee access",
|
||||||
|
"description": "Can create, edit, and deactivate limited backoffice employees."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Additional capability",
|
||||||
|
"description": "This capability is provided by the role catalog but is not yet described in the interface."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,6 +385,7 @@
|
|||||||
"delete": "@:{'templates.generated.compat.global.delete'}",
|
"delete": "@:{'templates.generated.compat.global.delete'}",
|
||||||
"details": "@:{'templates.generated.compat.admin.pos.details'}",
|
"details": "@:{'templates.generated.compat.admin.pos.details'}",
|
||||||
"download": "@:{'templates.generated.compat.guest.pwa.title'}",
|
"download": "@:{'templates.generated.compat.guest.pwa.title'}",
|
||||||
|
"ean": "@:{'templates.generated.compat.admin.pos.ean'}",
|
||||||
"drafts_assignment": {
|
"drafts_assignment": {
|
||||||
"button": "@:{'templates.generated.compat.admin.pos.drafts_assignment.button'}",
|
"button": "@:{'templates.generated.compat.admin.pos.drafts_assignment.button'}",
|
||||||
"create_invoice_collection": "@:{'templates.generated.compat.admin.pos.drafts_assignment.create_invoice_collection'}",
|
"create_invoice_collection": "@:{'templates.generated.compat.admin.pos.drafts_assignment.create_invoice_collection'}",
|
||||||
@@ -2061,6 +2062,7 @@
|
|||||||
"benefit_wash": "@:{'templates.generated.compat.tables.bookings.wash'}",
|
"benefit_wash": "@:{'templates.generated.compat.tables.bookings.wash'}",
|
||||||
"benefit_wash_desc": "@:{'templates.generated.compat.tables.bookings.wash_desc'}",
|
"benefit_wash_desc": "@:{'templates.generated.compat.tables.bookings.wash_desc'}",
|
||||||
"cvr_label": "@:{'templates.generated.compat.customers.cvr'}",
|
"cvr_label": "@:{'templates.generated.compat.customers.cvr'}",
|
||||||
|
"ean_label": "@:{'templates.generated.compat.customer_creation.customer.ean_label'}",
|
||||||
"email_label": "@:{'templates.generated.compat.customer_creation.customer.email_label'}",
|
"email_label": "@:{'templates.generated.compat.customer_creation.customer.email_label'}",
|
||||||
"intro": "@:{'templates.generated.compat.customer_creation.customer.intro'}",
|
"intro": "@:{'templates.generated.compat.customer_creation.customer.intro'}",
|
||||||
"phone_label": "@:{'templates.generated.compat.customer_creation.customer.phone_label'}",
|
"phone_label": "@:{'templates.generated.compat.customer_creation.customer.phone_label'}",
|
||||||
@@ -2420,6 +2422,13 @@
|
|||||||
"title": "@:{'templates.generated.compat.departments.tab.opening_hours'}"
|
"title": "@:{'templates.generated.compat.departments.tab.opening_hours'}"
|
||||||
},
|
},
|
||||||
"phone": "@:common.phone",
|
"phone": "@:common.phone",
|
||||||
|
"pricing": {
|
||||||
|
"custom_pricing_disabled": "@:{'templates.generated.compat.departments.pricing.custom_pricing_disabled'}",
|
||||||
|
"custom_pricing_enabled": "@:{'templates.generated.compat.departments.pricing.custom_pricing_enabled'}",
|
||||||
|
"custom_pricing_missing_price": "@:{'templates.generated.compat.departments.pricing.custom_pricing_missing_price'}",
|
||||||
|
"custom_pricing_only": "@:{'templates.generated.compat.departments.pricing.custom_pricing_only'}",
|
||||||
|
"effective_department_price": "@:{'templates.generated.compat.departments.pricing.effective_department_price'}"
|
||||||
|
},
|
||||||
"products": {
|
"products": {
|
||||||
"new_product": "@:products.new_product"
|
"new_product": "@:products.new_product"
|
||||||
},
|
},
|
||||||
@@ -3149,6 +3158,15 @@
|
|||||||
"preview_title": "@:{'templates.generated.compat.invoicing_period.monthly_split.preview_title'}",
|
"preview_title": "@:{'templates.generated.compat.invoicing_period.monthly_split.preview_title'}",
|
||||||
"success_text": "@:{'templates.generated.compat.invoicing_period.monthly_split.success_text'}",
|
"success_text": "@:{'templates.generated.compat.invoicing_period.monthly_split.success_text'}",
|
||||||
"success_title": "@:{'templates.generated.compat.invoicing_period.monthly_split.success_title'}"
|
"success_title": "@:{'templates.generated.compat.invoicing_period.monthly_split.success_title'}"
|
||||||
|
},
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.invoice_together'}",
|
||||||
|
"split_by_month": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.split_by_month'}",
|
||||||
|
"split_error_title": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.split_error_title'}",
|
||||||
|
"split_success_text": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.split_success_text'}",
|
||||||
|
"split_success_title": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.split_success_title'}",
|
||||||
|
"text": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.text'}",
|
||||||
|
"title": "@:{'templates.generated.compat.invoicing_period.multi_month_invoice_warning.title'}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoicing": {
|
"invoicing": {
|
||||||
@@ -3486,6 +3504,7 @@
|
|||||||
"departments": {
|
"departments": {
|
||||||
"columns": {
|
"columns": {
|
||||||
"archived": "@:{'templates.generated.compat.objects.departments.columns.archived'}",
|
"archived": "@:{'templates.generated.compat.objects.departments.columns.archived'}",
|
||||||
|
"custom_pricing_only": "@:{'templates.generated.compat.objects.departments.columns.custom_pricing_only'}",
|
||||||
"dimension": "@:{'templates.generated.compat.objects.departments.columns.dimension'}",
|
"dimension": "@:{'templates.generated.compat.objects.departments.columns.dimension'}",
|
||||||
"economic_department": "@:{'templates.generated.compat.objects.departments.columns.economic_department'}",
|
"economic_department": "@:{'templates.generated.compat.objects.departments.columns.economic_department'}",
|
||||||
"latitude": "@:{'templates.generated.compat.objects.departments.columns.latitude'}",
|
"latitude": "@:{'templates.generated.compat.objects.departments.columns.latitude'}",
|
||||||
@@ -4255,6 +4274,97 @@
|
|||||||
"title": "@:replication.labels.replication"
|
"title": "@:replication.labels.replication"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "@:{'templates.generated.compat.roles.permissions.access_scope'}",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "@:{'templates.generated.compat.roles.permissions.actions.clear_all'}",
|
||||||
|
"reload": "@:{'templates.generated.compat.roles.permissions.actions.reload'}",
|
||||||
|
"retry": "@:{'templates.generated.compat.roles.permissions.actions.retry'}",
|
||||||
|
"select_all": "@:{'templates.generated.compat.roles.permissions.actions.select_all'}"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "@:{'templates.generated.compat.roles.permissions.columns.enabled'}",
|
||||||
|
"permission": "@:{'templates.generated.compat.roles.permissions.columns.permission'}",
|
||||||
|
"route": "@:{'templates.generated.compat.roles.permissions.columns.route'}",
|
||||||
|
"status": "@:{'templates.generated.compat.roles.permissions.columns.status'}"
|
||||||
|
},
|
||||||
|
"department_access_description": "@:{'templates.generated.compat.roles.permissions.department_access_description'}",
|
||||||
|
"empty": "@:{'templates.generated.compat.roles.permissions.empty'}",
|
||||||
|
"empty_group": "@:{'templates.generated.compat.roles.permissions.empty_group'}",
|
||||||
|
"errors": {
|
||||||
|
"load": "@:{'templates.generated.compat.roles.permissions.errors.load'}",
|
||||||
|
"update": "@:{'templates.generated.compat.roles.permissions.errors.update'}"
|
||||||
|
},
|
||||||
|
"filtered_summary": "@:{'templates.generated.compat.roles.permissions.filtered_summary'}",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.access.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.access.label'}"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.bookings.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.bookings.label'}"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.customers_users.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.customers_users.label'}"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.departments_operations.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.departments_operations.label'}"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.invoicing_economic.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.invoicing_economic.label'}"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.orders_pos.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.orders_pos.label'}"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.other.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.other.label'}"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.products_pricing.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.products_pricing.label'}"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.selfserve_edge.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.selfserve_edge.label'}"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.groups.system_config.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.groups.system_config.label'}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "@:{'templates.generated.compat.roles.permissions.no_description'}",
|
||||||
|
"no_search_results": "@:{'templates.generated.compat.roles.permissions.no_search_results'}",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.page_access.admin.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.page_access.admin.label'}"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.page_access.superuser.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.page_access.superuser.label'}"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "@:{'templates.generated.compat.roles.permissions.page_access.user.description'}",
|
||||||
|
"label": "@:{'templates.generated.compat.roles.permissions.page_access.user.label'}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "@:{'templates.generated.compat.roles.permissions.search_label'}",
|
||||||
|
"search_placeholder": "@:{'templates.generated.compat.roles.permissions.search_placeholder'}",
|
||||||
|
"selected_summary": "@:{'templates.generated.compat.roles.permissions.selected_summary'}",
|
||||||
|
"status": {
|
||||||
|
"disabled": "@:{'templates.generated.compat.roles.permissions.status.disabled'}",
|
||||||
|
"enabled": "@:{'templates.generated.compat.roles.permissions.status.enabled'}"
|
||||||
|
},
|
||||||
|
"subtitle": "@:{'templates.generated.compat.roles.permissions.subtitle'}",
|
||||||
|
"title": "@:{'templates.generated.compat.roles.permissions.title'}",
|
||||||
|
"title_with_name": "@:{'templates.generated.compat.roles.permissions.title_with_name'}"
|
||||||
|
},
|
||||||
"subtitle": "@:{'templates.generated.compat.roles.subtitle'}",
|
"subtitle": "@:{'templates.generated.compat.roles.subtitle'}",
|
||||||
"title": "@:{'templates.generated.compat.common.roles'}"
|
"title": "@:{'templates.generated.compat.common.roles'}"
|
||||||
},
|
},
|
||||||
@@ -4468,6 +4578,82 @@
|
|||||||
"subtitle": "@:{'templates.generated.compat.superuser_dashboard.departments.subtitle'}",
|
"subtitle": "@:{'templates.generated.compat.superuser_dashboard.departments.subtitle'}",
|
||||||
"title": "@:common.departments"
|
"title": "@:common.departments"
|
||||||
},
|
},
|
||||||
|
"department_navigation": {
|
||||||
|
"overview": "Overview",
|
||||||
|
"modules": "Modules",
|
||||||
|
"branding": "Profile & Branding",
|
||||||
|
"gateways": "Gateways",
|
||||||
|
"stripe": "Stripe",
|
||||||
|
"pricing": "Pricing",
|
||||||
|
"categories": "Categories"
|
||||||
|
},
|
||||||
|
"department_overview": {
|
||||||
|
"title": "Department overview",
|
||||||
|
"subtitle": "Operational overview for the selected department",
|
||||||
|
"loading": "Loading department overview",
|
||||||
|
"date_from": "From",
|
||||||
|
"date_to": "To",
|
||||||
|
"range_label": "{from} to {to}",
|
||||||
|
"empty_value": "-",
|
||||||
|
"out_of": "of {total}",
|
||||||
|
"presets": {
|
||||||
|
"today": "Today",
|
||||||
|
"last_seven_days": "Last 7 days"
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"bookings": "Bookings",
|
||||||
|
"complaints": "Complaints",
|
||||||
|
"night_washes": "Night washes",
|
||||||
|
"overtime": "Overtime",
|
||||||
|
"products_sold": "Products sold",
|
||||||
|
"revenue": "Revenue",
|
||||||
|
"transactions": "Transactions",
|
||||||
|
"washes": "Washes",
|
||||||
|
"water_usage": "Water"
|
||||||
|
},
|
||||||
|
"units": {
|
||||||
|
"hours": "h",
|
||||||
|
"liters": "L"
|
||||||
|
},
|
||||||
|
"products": {
|
||||||
|
"title": "Product mix",
|
||||||
|
"subtitle": "Tracked wash products in the selected period",
|
||||||
|
"empty": "No product activity for the selected period"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "Department profile",
|
||||||
|
"no_description": "No department description",
|
||||||
|
"department_id": "Department ID",
|
||||||
|
"economic_department_id": "Economic department",
|
||||||
|
"branding": "Branding",
|
||||||
|
"created_at": "Created",
|
||||||
|
"updated_at": "Updated"
|
||||||
|
},
|
||||||
|
"hardware": {
|
||||||
|
"title": "Hardware readiness",
|
||||||
|
"subtitle": "Gateway-backed lane and relay state",
|
||||||
|
"gateways": "Gateways online",
|
||||||
|
"lanes": "Lanes",
|
||||||
|
"gates": "Gates",
|
||||||
|
"relays": "Relays",
|
||||||
|
"scanners": "Scanners",
|
||||||
|
"issues": "Issues"
|
||||||
|
},
|
||||||
|
"quick_links": {
|
||||||
|
"title": "Department tools",
|
||||||
|
"subtitle": "Open the focused setup areas for this department",
|
||||||
|
"modules": "Modules",
|
||||||
|
"branding": "Branding",
|
||||||
|
"gateways": "Gateways",
|
||||||
|
"stripe": "Stripe",
|
||||||
|
"pricing": "Pricing",
|
||||||
|
"categories": "Categories"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"invalid_department": "A valid department is required",
|
||||||
|
"load": "Unable to load the department overview"
|
||||||
|
}
|
||||||
|
},
|
||||||
"employees": {
|
"employees": {
|
||||||
"edit_employee_title": "@:employees.edit_employee",
|
"edit_employee_title": "@:employees.edit_employee",
|
||||||
"new_employee_subtitle": "@:{'templates.generated.compat.superuser_dashboard.employees.new_employee_subtitle'}",
|
"new_employee_subtitle": "@:{'templates.generated.compat.superuser_dashboard.employees.new_employee_subtitle'}",
|
||||||
@@ -5446,6 +5632,31 @@
|
|||||||
"transaction_history": "@:{'templates.generated.compat.user_admin.orders.transaction_history'}"
|
"transaction_history": "@:{'templates.generated.compat.user_admin.orders.transaction_history'}"
|
||||||
},
|
},
|
||||||
"other": "@:{'templates.generated.compat.user_admin.other'}",
|
"other": "@:{'templates.generated.compat.user_admin.other'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "@:{'templates.generated.compat.user_admin.overview.active_vehicles'}",
|
||||||
|
"attributes": "@:{'templates.generated.compat.user_admin.overview.attributes'}",
|
||||||
|
"connected": "@:{'templates.generated.compat.user_admin.overview.connected'}",
|
||||||
|
"customer_management": "@:{'templates.generated.compat.user_admin.overview.customer_management'}",
|
||||||
|
"customer_number_required": "@:{'templates.generated.compat.user_admin.overview.customer_number_required'}",
|
||||||
|
"customer_rules": "@:{'templates.generated.compat.user_admin.overview.customer_rules'}",
|
||||||
|
"effective_access": "@:{'templates.generated.compat.user_admin.overview.effective_access'}",
|
||||||
|
"loaded": "@:{'templates.generated.compat.user_admin.overview.loaded'}",
|
||||||
|
"load_failed": "@:{'templates.generated.compat.user_admin.overview.load_failed'}",
|
||||||
|
"loading_user": "@:{'templates.generated.compat.user_admin.overview.loading_user'}",
|
||||||
|
"management_hub": "@:{'templates.generated.compat.user_admin.overview.management_hub'}",
|
||||||
|
"missing": "@:{'templates.generated.compat.user_admin.overview.missing'}",
|
||||||
|
"more_permissions": "@:{'templates.generated.compat.user_admin.overview.more_permissions'}",
|
||||||
|
"no_subscription_transactions": "@:{'templates.generated.compat.user_admin.overview.no_subscription_transactions'}",
|
||||||
|
"not_invoiced": "@:{'templates.generated.compat.user_admin.overview.not_invoiced'}",
|
||||||
|
"open_draft": "@:{'templates.generated.compat.user_admin.overview.open_draft'}",
|
||||||
|
"open_vehicles": "@:{'templates.generated.compat.user_admin.overview.open_vehicles'}",
|
||||||
|
"price_overrides": "@:{'templates.generated.compat.user_admin.overview.price_overrides'}",
|
||||||
|
"pricing": "@:{'templates.generated.compat.user_admin.overview.pricing'}",
|
||||||
|
"subscription_invoicing": "@:{'templates.generated.compat.user_admin.overview.subscription_invoicing'}",
|
||||||
|
"subtitle": "@:{'templates.generated.compat.user_admin.overview.subtitle'}",
|
||||||
|
"vehicles_load_failed": "@:{'templates.generated.compat.user_admin.overview.vehicles_load_failed'}",
|
||||||
|
"workspace_shortcuts": "@:{'templates.generated.compat.user_admin.overview.workspace_shortcuts'}"
|
||||||
|
},
|
||||||
"permissions": "@:{'templates.generated.compat.user_admin.permissions'}",
|
"permissions": "@:{'templates.generated.compat.user_admin.permissions'}",
|
||||||
"subtitle": "@:user_admin.user_data",
|
"subtitle": "@:user_admin.user_data",
|
||||||
"title": "@:{'templates.generated.compat.common.user'}",
|
"title": "@:{'templates.generated.compat.common.user'}",
|
||||||
@@ -5743,6 +5954,20 @@
|
|||||||
"vehicles": {
|
"vehicles": {
|
||||||
"actions": "@:common.actions",
|
"actions": "@:common.actions",
|
||||||
"add": "@:{'templates.generated.compat.vehicles.add'}",
|
"add": "@:{'templates.generated.compat.vehicles.add'}",
|
||||||
|
"add_modal": {
|
||||||
|
"customer_label": "@:{'templates.generated.compat.vehicles.add_modal.customer_label'}",
|
||||||
|
"customer_placeholder": "@:{'templates.generated.compat.vehicles.add_modal.customer_placeholder'}",
|
||||||
|
"error": "@:{'templates.generated.compat.vehicles.add_modal.error'}",
|
||||||
|
"no_customer_results": "@:{'templates.generated.compat.vehicles.add_modal.no_customer_results'}",
|
||||||
|
"reference_placeholder": "@:{'templates.generated.compat.vehicles.add_modal.reference_placeholder'}",
|
||||||
|
"registration_placeholder": "@:{'templates.generated.compat.vehicles.add_modal.registration_placeholder'}",
|
||||||
|
"selected_customer": "@:{'templates.generated.compat.vehicles.add_modal.selected_customer'}",
|
||||||
|
"submit": "@:{'templates.generated.compat.vehicles.add_modal.submit'}",
|
||||||
|
"title": "@:{'templates.generated.compat.vehicles.add_modal.title'}",
|
||||||
|
"type_load_error": "@:{'templates.generated.compat.vehicles.add_modal.type_load_error'}",
|
||||||
|
"type_placeholder": "@:{'templates.generated.compat.vehicles.add_modal.type_placeholder'}",
|
||||||
|
"validation_error": "@:{'templates.generated.compat.vehicles.add_modal.validation_error'}"
|
||||||
|
},
|
||||||
"brand": "@:{'templates.generated.compat.vehicles.brand'}",
|
"brand": "@:{'templates.generated.compat.vehicles.brand'}",
|
||||||
"color": "@:{'templates.generated.compat.vehicles.color'}",
|
"color": "@:{'templates.generated.compat.vehicles.color'}",
|
||||||
"created_at": "@:{'templates.generated.compat.global.generated'}",
|
"created_at": "@:{'templates.generated.compat.global.generated'}",
|
||||||
|
|||||||
@@ -1290,6 +1290,9 @@
|
|||||||
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
||||||
|
"autosave_saving": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
|
"autosave_pending": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
|
"autosave_saved": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
||||||
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
||||||
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
||||||
@@ -1304,6 +1307,15 @@
|
|||||||
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'templates.generated.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'templates.generated.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'templates.generated.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -1322,7 +1334,13 @@
|
|||||||
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'templates.generated.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'templates.generated.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -1352,7 +1370,132 @@
|
|||||||
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'templates.generated.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'templates.generated.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'templates.generated.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'templates.generated.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'templates.generated.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'templates.generated.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'templates.generated.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'templates.generated.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -1682,6 +1825,7 @@
|
|||||||
"company_phone": "Firmatelefon",
|
"company_phone": "Firmatelefon",
|
||||||
"contact_email": "@.capitalize:{'words.generated.kontakt'} @:{'words.generated.e'}-@:{'words.generated.post'}",
|
"contact_email": "@.capitalize:{'words.generated.kontakt'} @:{'words.generated.e'}-@:{'words.generated.post'}",
|
||||||
"contact_phone": "@.capitalize:{'words.generated.kontakt'} @:{'words.generated.telefon'}",
|
"contact_phone": "@.capitalize:{'words.generated.kontakt'} @:{'words.generated.telefon'}",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "Laget @:{'words.generated.av'}",
|
"created_by": "Laget @:{'words.generated.av'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'words.generated.det'} @:{'words.generated.innskrevne'} @:{'words.generated.skiltet'} @:{'words.generated.tilhoerer'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.annen'} @:{'words.generated.lagret'} @:{'words.generated.kunde'}. @.capitalize:{'words.generated.velg'} @:{'words.generated.om'} @:{'words.replication.article.host_mention'} @:{'words.generated.valgte'} @:{'words.generated.kunden'} @:{'words.generated.skal'} @:{'words.generated.beholdes'} @:{'words.generated.eller'} @:{'words.generated.om'} @:{'words.generated.det'} @:{'words.generated.skal'} @:{'words.generated.byttes'} @:{'words.generated.til'} @:{'words.generated.kjoretoykunden'}.",
|
"help_text": "@.capitalize:{'words.generated.det'} @:{'words.generated.innskrevne'} @:{'words.generated.skiltet'} @:{'words.generated.tilhoerer'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.annen'} @:{'words.generated.lagret'} @:{'words.generated.kunde'}. @.capitalize:{'words.generated.velg'} @:{'words.generated.om'} @:{'words.replication.article.host_mention'} @:{'words.generated.valgte'} @:{'words.generated.kunden'} @:{'words.generated.skal'} @:{'words.generated.beholdes'} @:{'words.generated.eller'} @:{'words.generated.om'} @:{'words.generated.det'} @:{'words.generated.skal'} @:{'words.generated.byttes'} @:{'words.generated.til'} @:{'words.generated.kjoretoykunden'}.",
|
||||||
@@ -3230,6 +3374,7 @@
|
|||||||
"benefit_portal": "@:{'words.generated.kundeportal'}",
|
"benefit_portal": "@:{'words.generated.kundeportal'}",
|
||||||
"benefit_portal_desc": "@.capitalize:{'words.generated.bruk'} kundeportalen @:{'words.generated.til'} @:{'words.generated.a'} @:{'words.generated.legge'} @:{'words.generated.til'} @:{'words.generated.kjøretøy'}, bokvaske, @:{'words.generated.laste'} @:{'words.generated.ned'} @:{'words.generated.fakturaer'}, @:{'words.generated.vaskesertifikater'} @:{'words.generated.og'} @:{'words.generated.informasjon'} @:{'words.generated.for'} @:{'words.generated.historiske'} @:{'words.generated.vask'}.",
|
"benefit_portal_desc": "@.capitalize:{'words.generated.bruk'} kundeportalen @:{'words.generated.til'} @:{'words.generated.a'} @:{'words.generated.legge'} @:{'words.generated.til'} @:{'words.generated.kjøretøy'}, bokvaske, @:{'words.generated.laste'} @:{'words.generated.ned'} @:{'words.generated.fakturaer'}, @:{'words.generated.vaskesertifikater'} @:{'words.generated.og'} @:{'words.generated.informasjon'} @:{'words.generated.for'} @:{'words.generated.historiske'} @:{'words.generated.vask'}.",
|
||||||
"benefit_wash_desc": "@.capitalize:{'words.generated.bruk'} @:{'words.generated.vart'} @:{'words.generated.nasjonale'} @:{'words.generated.nettverk'} @:{'words.generated.av'} @:{'words.generated.vaskehaller'} @:{'words.generated.til'} @:{'words.generated.konkurransedyktige'} @:{'words.generated.priser'}",
|
"benefit_wash_desc": "@.capitalize:{'words.generated.bruk'} @:{'words.generated.vart'} @:{'words.generated.nasjonale'} @:{'words.generated.nettverk'} @:{'words.generated.av'} @:{'words.generated.vaskehaller'} @:{'words.generated.til'} @:{'words.generated.konkurransedyktige'} @:{'words.generated.priser'}",
|
||||||
|
"ean_label": "@:{'templates.generated.compat.admin.pos.ean'}",
|
||||||
"email_label": "Firmaets @:{'words.generated.e'}-@:{'words.generated.post'}",
|
"email_label": "Firmaets @:{'words.generated.e'}-@:{'words.generated.post'}",
|
||||||
"intro": "@.capitalize:{'words.generated.som'} @:{'words.generated.kunde'} @:{'words.generated.hos'} @:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.far'} @:{'words.generated.du'} @:{'words.generated.tilgang'} @:{'words.generated.til'}:",
|
"intro": "@.capitalize:{'words.generated.som'} @:{'words.generated.kunde'} @:{'words.generated.hos'} @:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.far'} @:{'words.generated.du'} @:{'words.generated.tilgang'} @:{'words.generated.til'}:",
|
||||||
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
||||||
@@ -3468,17 +3613,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Rapporter feil",
|
"button": "Rapporter feil",
|
||||||
"title": "Rapporter feil",
|
"title": "Rapporter feil",
|
||||||
"subtitle": "Send gjeldende skjermbilde og de siste feildetaljene til support.",
|
"subtitle": "Send de siste feildetaljene til support. Et skjermbilde legges ved når det er mulig.",
|
||||||
"before_error": "Hva gjorde du før feilen oppstod?",
|
"before_error": "Hva gjorde du før feilen oppstod?",
|
||||||
"expected": "Hva forventet du at skulle skje?",
|
"expected": "Hva forventet du at skulle skje?",
|
||||||
"actual": "Hva skjedde faktisk?",
|
"actual": "Hva skjedde faktisk?",
|
||||||
"before_error_placeholder": "Beskriv handlingen du utførte, for eksempel å åpne ordre eller velge en kunde.",
|
"before_error_placeholder": "Beskriv handlingen du utførte, for eksempel å åpne ordre eller velge en kunde.",
|
||||||
"expected_placeholder": "Beskriv resultatet du forventet å se.",
|
"expected_placeholder": "Beskriv resultatet du forventet å se.",
|
||||||
"actual_placeholder": "Beskriv hva du så i stedet, inkludert eventuell feiltekst.",
|
"actual_placeholder": "Beskriv hva du så i stedet, inkludert eventuell feiltekst.",
|
||||||
"consent": "Jeg godtar at gjeldende app-skjermbilde, siste request-feil, Vue-feil, nettleserdetaljer og svarene mine samles inn for feilsøking.",
|
"consent": "Jeg godtar at siste request-feil, Vue-feil, nettleserdetaljer, svarene mine og et app-skjermbilde når det er mulig, samles inn for feilsøking.",
|
||||||
"submit": "Send rapport",
|
"submit": "Send rapport",
|
||||||
"submitted": "Feilrapport sendt.",
|
"submitted": "Feilrapport sendt.",
|
||||||
"capture_failed": "Skjermbildet kunne ikke tas. Prøv igjen.",
|
"capture_failed": "Skjermbildet kunne ikke tas. Rapporten sendes uten skjermbilde.",
|
||||||
"submit_failed": "Feilrapporten kunne ikke sendes.",
|
"submit_failed": "Feilrapporten kunne ikke sendes.",
|
||||||
"required": "Alle felter og godkjenning av datainnsamling er påkrevd.",
|
"required": "Alle felter og godkjenning av datainnsamling er påkrevd.",
|
||||||
"page_subtitle": "Rapporterte skjermer, request-feil og Vue-feil",
|
"page_subtitle": "Rapporterte skjermer, request-feil og Vue-feil",
|
||||||
@@ -4018,6 +4163,15 @@
|
|||||||
"preview_title": "Preview @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
"preview_title": "Preview @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
||||||
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
||||||
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
||||||
|
},
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Fakturer samlet",
|
||||||
|
"split_by_month": "Del opp etter måned",
|
||||||
|
"split_error_title": "Månedsdeling mislyktes",
|
||||||
|
"split_success_text": "Behandlet {processed} fakturasamlinger. Delte opp {changed}, hoppet over {skipped}.",
|
||||||
|
"split_success_title": "Månedsdeling fullført",
|
||||||
|
"text": "Du er i ferd med å fakturere ordrer fra flere måneder samlet ({months}). Skal de i stedet deles opp etter måned?",
|
||||||
|
"title": "Ordrer fra flere måneder"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoicing": {
|
"invoicing": {
|
||||||
@@ -4836,6 +4990,97 @@
|
|||||||
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.replication.services.database'} @:{'words.generated.and'} @:{'words.replication.services.redis'} @:{'words.generated.replication'}"
|
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.replication.services.database'} @:{'words.generated.and'} @:{'words.replication.services.redis'} @:{'words.generated.replication'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Tilgangsomrade",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Tomm gruppe",
|
||||||
|
"reload": "Last inn pa nytt",
|
||||||
|
"retry": "Prov igjen",
|
||||||
|
"select_all": "Velg gruppe"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Aktivert",
|
||||||
|
"permission": "Tillatelse",
|
||||||
|
"route": "Rute",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Gir tilgang til {name}.",
|
||||||
|
"empty": "Ingen tillatelser er tilgjengelige.",
|
||||||
|
"empty_group": "Denne gruppen har ingen tillatelser.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Kunne ikke laste rolletillatelser.",
|
||||||
|
"update": "En eller flere tillatelser kunne ikke oppdateres. De gjeldende rolletillatelsene ble lastet inn pa nytt."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} treff",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Sidetype- og avdelingstilgang.",
|
||||||
|
"label": "Tilgang"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Tillatelser for bookinger, tidsbookinger og planlegging.",
|
||||||
|
"label": "Bookinger"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Tillatelser for kunder, brukere, ansatte, roller og innlogging.",
|
||||||
|
"label": "Kunder og brukere"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Avdelingsdrift, baner, releer, varsler, rapporter og mal.",
|
||||||
|
"label": "Avdelinger og drift"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Tillatelser for fakturaer, faktureringsperioder, innkreving og e-conomic.",
|
||||||
|
"label": "Fakturering og e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Tillatelser for ordrer, POS, betalinger og ordrelinjer.",
|
||||||
|
"label": "Ordrer og POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Tillatelser som ikke matcher en annen gruppe.",
|
||||||
|
"label": "Annet"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Tillatelser for produkter, priser, rabatter og valuta.",
|
||||||
|
"label": "Produkter og priser"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Tillatelser for selvbetjening, vask, skannere, gatewayer og edge-utstyr.",
|
||||||
|
"label": "Selvbetjening og edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, konfigurasjon, release, replikering og integrasjoner.",
|
||||||
|
"label": "System og konfigurasjon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "Ingen beskrivelse er tilgjengelig.",
|
||||||
|
"no_search_results": "Ingen tillatelser matcher soket.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Gir tilgang til administrasjonssider.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Gir tilgang til superbrukersider.",
|
||||||
|
"label": "Superbruker"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Gir tilgang til brukersider.",
|
||||||
|
"label": "Bruker"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Sok i tillatelser",
|
||||||
|
"search_placeholder": "Sok pa label, nokkel, beskrivelse, rute eller metode",
|
||||||
|
"selected_summary": "{selected} av {total} valgt",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Deaktivert",
|
||||||
|
"enabled": "Aktivert"
|
||||||
|
},
|
||||||
|
"subtitle": "Rolletillatelser",
|
||||||
|
"title": "Tillatelser for rolle {id}",
|
||||||
|
"title_with_name": "Tillatelser for {name}"
|
||||||
|
},
|
||||||
"subtitle": "@.capitalize:{'words.generated.user'} roles"
|
"subtitle": "@.capitalize:{'words.generated.user'} roles"
|
||||||
},
|
},
|
||||||
"self_wash": {
|
"self_wash": {
|
||||||
@@ -5625,6 +5870,31 @@
|
|||||||
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
||||||
},
|
},
|
||||||
"other": "@:{'templates.generated.compat.global.other'}",
|
"other": "@:{'templates.generated.compat.global.other'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Aktive kjøretøy",
|
||||||
|
"attributes": "Attributter",
|
||||||
|
"connected": "Tilkoblet",
|
||||||
|
"customer_management": "Kundestyring",
|
||||||
|
"customer_number_required": "Et kundenummer kreves før kundeinnstillinger kan administreres.",
|
||||||
|
"customer_rules": "Kunderegler",
|
||||||
|
"effective_access": "Effektiv tilgang",
|
||||||
|
"loaded": "Lastet",
|
||||||
|
"load_failed": "Brukerdata kunne ikke lastes",
|
||||||
|
"loading_user": "Laster bruker",
|
||||||
|
"management_hub": "Administrasjon",
|
||||||
|
"missing": "Mangler",
|
||||||
|
"more_permissions": "+{count} flere tillatelser",
|
||||||
|
"no_subscription_transactions": "Ingen vaskeabonnementstransaksjoner er registrert for kunden.",
|
||||||
|
"not_invoiced": "Ikke fakturert",
|
||||||
|
"open_draft": "Åpent utkast",
|
||||||
|
"open_vehicles": "Åpne kjøretøy",
|
||||||
|
"price_overrides": "Prisoverstyringer",
|
||||||
|
"pricing": "Priser",
|
||||||
|
"subscription_invoicing": "Abonnementsfakturering",
|
||||||
|
"subtitle": "Oversikt over kundedata, tilgang, priser, kjøretøy og abonnementsfakturering.",
|
||||||
|
"vehicles_load_failed": "Kjøretøy kunne ikke lastes.",
|
||||||
|
"workspace_shortcuts": "Arbeidsområde"
|
||||||
|
},
|
||||||
"permissions": "@.capitalize:{'words.generated.tillatelser'}",
|
"permissions": "@.capitalize:{'words.generated.tillatelser'}",
|
||||||
"user_data": "@.capitalize:{'words.generated.brukerdata'}",
|
"user_data": "@.capitalize:{'words.generated.brukerdata'}",
|
||||||
"user_id": "@.capitalize:{'words.generated.bruker'}-@.upper:{'words.generated.id'}",
|
"user_id": "@.capitalize:{'words.generated.bruker'}-@.upper:{'words.generated.id'}",
|
||||||
@@ -5895,6 +6165,9 @@
|
|||||||
"title": "@.capitalize:{'words.generated.avdelingspriser'}",
|
"title": "@.capitalize:{'words.generated.avdelingspriser'}",
|
||||||
"save": "@.capitalize:{'words.generated.lagre'} @:{'words.generated.priser'}",
|
"save": "@.capitalize:{'words.generated.lagre'} @:{'words.generated.priser'}",
|
||||||
"saved": "Prisene @:{'words.generated.er'} @:{'words.generated.lagret'}.",
|
"saved": "Prisene @:{'words.generated.er'} @:{'words.generated.lagret'}.",
|
||||||
|
"autosave_saving": "Lagrer priser...",
|
||||||
|
"autosave_pending": "Prisendringer venter.",
|
||||||
|
"autosave_saved": "Alle prisendringer er lagret.",
|
||||||
"product": "Produkt",
|
"product": "Produkt",
|
||||||
"price": "Avdelingspris",
|
"price": "Avdelingspris",
|
||||||
"price_required": "@.capitalize:{'words.generated.alle'} @:{'words.generated.produkter'} @:{'words.generated.ma'} ha @:{'words.replication.host_definite_suffix'} @:{'words.generated.ikke'}-negativ numerisk pris.",
|
"price_required": "@.capitalize:{'words.generated.alle'} @:{'words.generated.produkter'} @:{'words.generated.ma'} ha @:{'words.replication.host_definite_suffix'} @:{'words.generated.ikke'}-negativ numerisk pris.",
|
||||||
@@ -5909,6 +6182,15 @@
|
|||||||
"edit_title": "@:{'words.generated.rediger'} @:{'words.generated.medarbeider'}",
|
"edit_title": "@:{'words.generated.rediger'} @:{'words.generated.medarbeider'}",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
"email": "E-post",
|
"email": "E-post",
|
||||||
|
"user_id": "Bruker-ID",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"phone_validation": "Telefonnummeret må være 4-15 sifre.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Danmark",
|
||||||
|
"sweden": "Sverige",
|
||||||
|
"norway": "Norge",
|
||||||
|
"finland": "Finland"
|
||||||
|
},
|
||||||
"password": "@.capitalize:{'words.generated.passord'}",
|
"password": "@.capitalize:{'words.generated.passord'}",
|
||||||
"password_create_help": "@.capitalize:{'words.generated.kreves'} @:{'words.generated.for'} nye @:{'words.generated.medarbeidere'}. Bruk @:{'words.generated.minst'} 8 tegn.",
|
"password_create_help": "@.capitalize:{'words.generated.kreves'} @:{'words.generated.for'} nye @:{'words.generated.medarbeidere'}. Bruk @:{'words.generated.minst'} 8 tegn.",
|
||||||
"password_update_help": "La stå tomt @:{'words.generated.for'} å beholde nåværende @:{'words.generated.passord'}.",
|
"password_update_help": "La stå tomt @:{'words.generated.for'} å beholde nåværende @:{'words.generated.passord'}.",
|
||||||
@@ -5927,7 +6209,13 @@
|
|||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"edit": "@:{'words.generated.rediger'}",
|
"edit": "@:{'words.generated.rediger'}",
|
||||||
"deactivate": "Deaktiver"
|
"deactivate": "Deaktiver",
|
||||||
|
"login_link": "QR-innlogging",
|
||||||
|
"login_link_title": "Innloggingslenke for medarbeider",
|
||||||
|
"login_link_qr_alt": "QR-kode for innloggingslenke",
|
||||||
|
"login_link_url": "Innloggingslenke",
|
||||||
|
"copy_login_link": "Kopier lenke",
|
||||||
|
"login_link_copied": "Lenken er kopiert."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -5957,7 +6245,132 @@
|
|||||||
"save_prices": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.lagre'} @:{'words.generated.avdelingspriser'}.",
|
"save_prices": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.lagre'} @:{'words.generated.avdelingspriser'}.",
|
||||||
"load_employees": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.laste'} @:{'words.generated.medarbeidere'}.",
|
"load_employees": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.laste'} @:{'words.generated.medarbeidere'}.",
|
||||||
"save_employee": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.lagre'} @:{'words.generated.medarbeideren'}.",
|
"save_employee": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} @:{'words.generated.lagre'} @:{'words.generated.medarbeideren'}.",
|
||||||
"deactivate_employee": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} deaktivere @:{'words.generated.medarbeideren'}."
|
"deactivate_employee": "@.capitalize:{'words.generated.kunne'} @:{'words.generated.ikke'} deaktivere @:{'words.generated.medarbeideren'}.",
|
||||||
|
"generate_employee_login_link": "Kunne ikke opprette innloggingslenke for medarbeideren."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Rolletillatelser",
|
||||||
|
"tooltip": "Vis rolletillatelser",
|
||||||
|
"intro": "Roller samler de nøyaktige funksjonene som tildeles en ansatt når rollen lagres.",
|
||||||
|
"scope": "Alle funksjoner er begrenset til avdelingene som velges for den ansatte.",
|
||||||
|
"selected": "Valgt",
|
||||||
|
"close": "Lukk",
|
||||||
|
"unavailable": "Tillatelsesdetaljer er ikke tilgjengelige for denne rollen før rollekatalogen er oppdatert.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Konto",
|
||||||
|
"orders": "Ordrer",
|
||||||
|
"bookings": "Bookinger",
|
||||||
|
"time_bookings": "Tidsbookinger",
|
||||||
|
"reports": "Rapporter",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Annet"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Logg inn",
|
||||||
|
"description": "Lar den ansatte logge inn i Pleno."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "Se egne tillatelser",
|
||||||
|
"description": "Viser den ansatte hvilke tilgangsrettigheter kontoen har."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "Se ordrer",
|
||||||
|
"description": "Kan åpne ordrelister og ordredetaljer for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Opprett ordrer",
|
||||||
|
"description": "Kan opprette nye ordrer for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Rediger ordrer",
|
||||||
|
"description": "Kan endre ordredetaljer for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Slett ordrer",
|
||||||
|
"description": "Kan slette ordrer i tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "Se ordrelinjer",
|
||||||
|
"description": "Kan se produkter, tjenester og priser på ordrer."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Opprett ordrelinjer",
|
||||||
|
"description": "Kan legge produkter og tjenester til ordrer."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Rediger ordrelinjer",
|
||||||
|
"description": "Kan endre produkter, antall og priser på ordrer."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Slett ordrelinjer",
|
||||||
|
"description": "Kan fjerne produkter og tjenester fra ordrer."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Belast ordrer",
|
||||||
|
"description": "Kan ta betaling eller belaste en betalingsreservasjon for en ordre."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "Se avdelingsbookinger",
|
||||||
|
"description": "Kan se bookinger for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "Se egne bookinger",
|
||||||
|
"description": "Kan se bookingantall og bookinger knyttet til ansattkontoen."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Rediger bookinger",
|
||||||
|
"description": "Kan endre tillatte bookingdetaljer."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Opprett bookinger",
|
||||||
|
"description": "Kan opprette nye bookinger for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Fullfør bookinger",
|
||||||
|
"description": "Kan markere bookinger som fullført."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Send bookingbekreftelser på nytt",
|
||||||
|
"description": "Kan sende bookingbekreftelser og fullførings-e-poster på nytt."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "Se tidsbookinger",
|
||||||
|
"description": "Kan gå gjennom tidsbookingoppføringer for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Opprett tidsbookinger",
|
||||||
|
"description": "Kan legge til nye tidsbookingoppføringer."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Rediger tidsbookinger",
|
||||||
|
"description": "Kan redigere tidsbookingoppføringer når backend-ruten støtter det."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "Se ordrestatistikk",
|
||||||
|
"description": "Kan åpne ordrestatistikk for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "Se bookingstatistikk",
|
||||||
|
"description": "Kan åpne bookingstatistikk for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Åpne begrenset backoffice",
|
||||||
|
"description": "Kan åpne det begrensede backofficeområdet."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Administrer avdelingspriser",
|
||||||
|
"description": "Kan redigere eksplisitte priser for tildelte avdelinger."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Administrer ansattilgang",
|
||||||
|
"description": "Kan opprette, redigere og deaktivere begrensede backofficeansatte."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Ekstra funksjon",
|
||||||
|
"description": "Denne funksjonen kommer fra rollekatalogen, men er ennå ikke beskrevet i grensesnittet."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1340,6 +1340,9 @@
|
|||||||
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'templates.generated.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'templates.generated.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'templates.generated.compat.limited_backoffice.prices.saved'}",
|
||||||
|
"autosave_saving": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
|
"autosave_pending": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
|
"autosave_saved": "@:{'templates.generated.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
"product": "@:{'templates.generated.compat.limited_backoffice.prices.product'}",
|
||||||
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
"price": "@:{'templates.generated.compat.limited_backoffice.prices.price'}",
|
||||||
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
"price_required": "@:{'templates.generated.compat.limited_backoffice.prices.price_required'}",
|
||||||
@@ -1354,6 +1357,15 @@
|
|||||||
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'templates.generated.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'templates.generated.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'templates.generated.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'templates.generated.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'templates.generated.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'templates.generated.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'templates.generated.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'templates.generated.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'templates.generated.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -1372,7 +1384,13 @@
|
|||||||
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'templates.generated.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'templates.generated.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'templates.generated.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'templates.generated.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'templates.generated.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'templates.generated.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'templates.generated.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -1402,7 +1420,132 @@
|
|||||||
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'templates.generated.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'templates.generated.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'templates.generated.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'templates.generated.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'templates.generated.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'templates.generated.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'templates.generated.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'templates.generated.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'templates.generated.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'templates.generated.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'templates.generated.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'templates.generated.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'templates.generated.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'templates.generated.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -1732,6 +1875,7 @@
|
|||||||
"company_phone": "Färetagstelefon",
|
"company_phone": "Färetagstelefon",
|
||||||
"contact_email": "@.capitalize:{'words.generated.kontakt'}-@:{'words.generated.e'}-@:{'words.generated.post'}",
|
"contact_email": "@.capitalize:{'words.generated.kontakt'}-@:{'words.generated.e'}-@:{'words.generated.post'}",
|
||||||
"contact_phone": "Kontakttelefon",
|
"contact_phone": "Kontakttelefon",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'words.generated.skapad'} @:{'words.generated.av'}",
|
"created_by": "@.capitalize:{'words.generated.skapad'} @:{'words.generated.av'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'words.generated.det'} @:{'words.generated.angivna'} @:{'words.generated.registreringsnumret'} @:{'words.generated.tillhor'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.annan'} @:{'words.generated.sparad'} @:{'words.generated.kund'}. @:{'words.generated.valj_2'} @:{'words.generated.om'} @:{'words.replication.article.host_mention'} @:{'words.generated.valda'} @:{'words.generated.kunden'} @:{'words.generated.ska'} @:{'words.generated.behallas'} @:{'words.generated.eller'} @:{'words.generated.om'} @:{'words.generated.bytet'} @:{'words.generated.ska'} @:{'words.generated.goras'} @:{'words.generated.till'} @:{'words.generated.fordonskunden'}.",
|
"help_text": "@.capitalize:{'words.generated.det'} @:{'words.generated.angivna'} @:{'words.generated.registreringsnumret'} @:{'words.generated.tillhor'} @:{'words.replication.host_definite_suffix'} @:{'words.generated.annan'} @:{'words.generated.sparad'} @:{'words.generated.kund'}. @:{'words.generated.valj_2'} @:{'words.generated.om'} @:{'words.replication.article.host_mention'} @:{'words.generated.valda'} @:{'words.generated.kunden'} @:{'words.generated.ska'} @:{'words.generated.behallas'} @:{'words.generated.eller'} @:{'words.generated.om'} @:{'words.generated.bytet'} @:{'words.generated.ska'} @:{'words.generated.goras'} @:{'words.generated.till'} @:{'words.generated.fordonskunden'}.",
|
||||||
@@ -3280,6 +3424,7 @@
|
|||||||
"benefit_portal": "@:{'words.generated.kundportal'}",
|
"benefit_portal": "@:{'words.generated.kundportal'}",
|
||||||
"benefit_portal_desc": "@.capitalize:{'words.generated.anvand'} kundportalen @:{'words.generated.for'} @:{'words.generated.att'} @:{'words.generated.lagga'} @:{'words.generated.till'} @:{'words.generated.fordon'}, @:{'words.generated.boka'} @:{'words.generated.tvattar'}, @:{'words.generated.ladda'} @:{'words.generated.ner'} @:{'words.generated.fakturor'}, @:{'words.generated.tvattcertifikat'} @:{'words.generated.och'} @:{'words.generated.information'} @:{'words.generated.om'} @:{'words.generated.historiska'} @:{'words.generated.tvattar'}",
|
"benefit_portal_desc": "@.capitalize:{'words.generated.anvand'} kundportalen @:{'words.generated.for'} @:{'words.generated.att'} @:{'words.generated.lagga'} @:{'words.generated.till'} @:{'words.generated.fordon'}, @:{'words.generated.boka'} @:{'words.generated.tvattar'}, @:{'words.generated.ladda'} @:{'words.generated.ner'} @:{'words.generated.fakturor'}, @:{'words.generated.tvattcertifikat'} @:{'words.generated.och'} @:{'words.generated.information'} @:{'words.generated.om'} @:{'words.generated.historiska'} @:{'words.generated.tvattar'}",
|
||||||
"benefit_wash_desc": "@.capitalize:{'words.generated.anvand'} @:{'words.generated.vart'} @:{'words.generated.nationella'} @:{'words.generated.natverk'} @:{'words.generated.av'} @:{'words.generated.tvatthallar'} @:{'words.generated.till'} @:{'words.generated.konkurrenskraftiga'} @:{'words.generated.priser'}",
|
"benefit_wash_desc": "@.capitalize:{'words.generated.anvand'} @:{'words.generated.vart'} @:{'words.generated.nationella'} @:{'words.generated.natverk'} @:{'words.generated.av'} @:{'words.generated.tvatthallar'} @:{'words.generated.till'} @:{'words.generated.konkurrenskraftiga'} @:{'words.generated.priser'}",
|
||||||
|
"ean_label": "@:{'templates.generated.compat.admin.pos.ean'}",
|
||||||
"email_label": "Företagets @:{'words.generated.e'}-@:{'words.generated.post'}",
|
"email_label": "Företagets @:{'words.generated.e'}-@:{'words.generated.post'}",
|
||||||
"intro": "@.capitalize:{'words.generated.som'} @:{'words.generated.kund'} @:{'words.generated.hos'} @:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.far'} @:{'words.generated.du'} @:{'words.generated.tillgang'} @:{'words.generated.till'}:",
|
"intro": "@.capitalize:{'words.generated.som'} @:{'words.generated.kund'} @:{'words.generated.hos'} @:{'words.generated.truck'} @.capitalize:{'words.generated.wash'} @:{'words.generated.far'} @:{'words.generated.du'} @:{'words.generated.tillgang'} @:{'words.generated.till'}:",
|
||||||
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
"phone_label": "@:{'templates.generated.compat.admin.pos.company_phone'}",
|
||||||
@@ -3518,17 +3663,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Rapportera fel",
|
"button": "Rapportera fel",
|
||||||
"title": "Rapportera fel",
|
"title": "Rapportera fel",
|
||||||
"subtitle": "Skicka aktuell skärm och de senaste feldetaljerna till support.",
|
"subtitle": "Skicka de senaste feldetaljerna till support. En skärmbild bifogas när det är möjligt.",
|
||||||
"before_error": "Vad gjorde du innan felet uppstod?",
|
"before_error": "Vad gjorde du innan felet uppstod?",
|
||||||
"expected": "Vad förväntade du dig skulle hända?",
|
"expected": "Vad förväntade du dig skulle hända?",
|
||||||
"actual": "Vad hände faktiskt?",
|
"actual": "Vad hände faktiskt?",
|
||||||
"before_error_placeholder": "Beskriv åtgärden du gjorde, till exempel att öppna ordrar eller välja en kund.",
|
"before_error_placeholder": "Beskriv åtgärden du gjorde, till exempel att öppna ordrar eller välja en kund.",
|
||||||
"expected_placeholder": "Beskriv resultatet du förväntade dig att se.",
|
"expected_placeholder": "Beskriv resultatet du förväntade dig att se.",
|
||||||
"actual_placeholder": "Beskriv vad du såg i stället, inklusive eventuell feltext.",
|
"actual_placeholder": "Beskriv vad du såg i stället, inklusive eventuell feltext.",
|
||||||
"consent": "Jag accepterar att aktuell app-skärm, senaste request-fel, Vue-fel, webbläsardetaljer och mina svar samlas in för felsökning.",
|
"consent": "Jag accepterar att senaste request-fel, Vue-fel, webbläsardetaljer, mina svar och en app-skärmbild när det är möjligt samlas in för felsökning.",
|
||||||
"submit": "Skicka rapport",
|
"submit": "Skicka rapport",
|
||||||
"submitted": "Felrapport skickad.",
|
"submitted": "Felrapport skickad.",
|
||||||
"capture_failed": "Skärmbilden kunde inte tas. Försök igen.",
|
"capture_failed": "Skärmbilden kunde inte tas. Rapporten skickas utan skärmbild.",
|
||||||
"submit_failed": "Felrapporten kunde inte skickas.",
|
"submit_failed": "Felrapporten kunde inte skickas.",
|
||||||
"required": "Alla fält och godkännande av datainsamling krävs.",
|
"required": "Alla fält och godkännande av datainsamling krävs.",
|
||||||
"page_subtitle": "Rapporterade skärmar, request-fel och Vue-fel",
|
"page_subtitle": "Rapporterade skärmar, request-fel och Vue-fel",
|
||||||
@@ -4068,6 +4213,15 @@
|
|||||||
"preview_title": "Preview @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
"preview_title": "Preview @:{'words.generated.monthly'} @:{'words.generated.split'}",
|
||||||
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
"success_text": "@:{'words.generated.processed'} {processed} @:{'words.generated.invoice'} @:{'words.generated.collections'}. @.capitalize:{'words.generated.split'} {changed}, @:{'words.generated.skipped'} {skipped}.",
|
||||||
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
"success_title": "@.capitalize:{'words.generated.monthly'} @:{'words.generated.split'} @:{'words.generated.completed'}"
|
||||||
|
},
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Fakturera tillsammans",
|
||||||
|
"split_by_month": "Dela upp per månad",
|
||||||
|
"split_error_title": "Månadsuppdelning misslyckades",
|
||||||
|
"split_success_text": "Bearbetade {processed} fakturasamlingar. Delade upp {changed}, hoppade över {skipped}.",
|
||||||
|
"split_success_title": "Månadsuppdelning klar",
|
||||||
|
"text": "Du håller på att fakturera ordrar från flera månader tillsammans ({months}). Ska de delas upp per månad i stället?",
|
||||||
|
"title": "Ordrar från flera månader"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoicing": {
|
"invoicing": {
|
||||||
@@ -4886,6 +5040,97 @@
|
|||||||
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.generated.database'} @:{'words.generated.and'} @:{'words.replication.services.redis'} @:{'words.generated.replication'}"
|
"subtitle": "@.capitalize:{'words.generated.managed'} @:{'words.generated.database'} @:{'words.generated.and'} @:{'words.replication.services.redis'} @:{'words.generated.replication'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Atkomstomrade",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Rensa grupp",
|
||||||
|
"reload": "Ladda om",
|
||||||
|
"retry": "Forsok igen",
|
||||||
|
"select_all": "Valj grupp"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Aktiverad",
|
||||||
|
"permission": "Behorighet",
|
||||||
|
"route": "Rutt",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Ger atkomst till {name}.",
|
||||||
|
"empty": "Inga behorigheter ar tillgangliga.",
|
||||||
|
"empty_group": "Den har gruppen har inga behorigheter.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Kunde inte lasa in rollbehorigheter.",
|
||||||
|
"update": "En eller flera behorigheter kunde inte uppdateras. Rollens aktuella behorigheter laddades om."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} traffar",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Sidtyp- och avdelningsatkomst.",
|
||||||
|
"label": "Atkomst"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Behorigheter for bokningar, tidsbokningar och planering.",
|
||||||
|
"label": "Bokningar"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Behorigheter for kunder, anvandare, medarbetare, roller och inloggning.",
|
||||||
|
"label": "Kunder och anvandare"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Avdelningsdrift, banor, relan, aviseringar, rapporter och mal.",
|
||||||
|
"label": "Avdelningar och drift"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Behorigheter for fakturor, faktureringsperioder, inkasso och e-conomic.",
|
||||||
|
"label": "Fakturering och e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Behorigheter for ordrar, POS, betalningar och orderrader.",
|
||||||
|
"label": "Ordrar och POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Behorigheter som inte matchar nagon annan grupp.",
|
||||||
|
"label": "Ovrigt"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Behorigheter for produkter, priser, rabatter och valuta.",
|
||||||
|
"label": "Produkter och priser"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Behorigheter for sjalvbetjaning, tvatt, skannrar, gateways och edge-utrustning.",
|
||||||
|
"label": "Sjalvbetjaning och edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, konfiguration, release, replikering och integrationer.",
|
||||||
|
"label": "System och konfiguration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "Ingen beskrivning ar tillganglig.",
|
||||||
|
"no_search_results": "Inga behorigheter matchar aktuell sokning.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Ger atkomst till administrationssidor.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Ger atkomst till superuser-sidor.",
|
||||||
|
"label": "Superuser"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Ger atkomst till anvandarsidor.",
|
||||||
|
"label": "Anvandare"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Sok behorigheter",
|
||||||
|
"search_placeholder": "Sok pa label, nyckel, beskrivning, rutt eller metod",
|
||||||
|
"selected_summary": "{selected} av {total} valda",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Inaktiverad",
|
||||||
|
"enabled": "Aktiverad"
|
||||||
|
},
|
||||||
|
"subtitle": "Rollbehorigheter",
|
||||||
|
"title": "Behorigheter for roll {id}",
|
||||||
|
"title_with_name": "Behorigheter for {name}"
|
||||||
|
},
|
||||||
"subtitle": "@.capitalize:{'words.generated.user'} @:{'words.generated.roles'}"
|
"subtitle": "@.capitalize:{'words.generated.user'} @:{'words.generated.roles'}"
|
||||||
},
|
},
|
||||||
"self_wash": {
|
"self_wash": {
|
||||||
@@ -5675,6 +5920,31 @@
|
|||||||
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
"transaction_history": "@:{'templates.generated.compat.superuser.nav.orders'}"
|
||||||
},
|
},
|
||||||
"other": "Andet",
|
"other": "Andet",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Aktiva fordon",
|
||||||
|
"attributes": "Attribut",
|
||||||
|
"connected": "Ansluten",
|
||||||
|
"customer_management": "Kundhantering",
|
||||||
|
"customer_number_required": "Ett kundnummer krävs innan kundinställningar kan hanteras.",
|
||||||
|
"customer_rules": "Kundregler",
|
||||||
|
"effective_access": "Effektiv åtkomst",
|
||||||
|
"loaded": "Inläst",
|
||||||
|
"load_failed": "Användardata kunde inte läsas in",
|
||||||
|
"loading_user": "Läser in användare",
|
||||||
|
"management_hub": "Administration",
|
||||||
|
"missing": "Saknas",
|
||||||
|
"more_permissions": "+{count} fler behörigheter",
|
||||||
|
"no_subscription_transactions": "Inga tvättabonnemangstransaktioner är registrerade för kunden.",
|
||||||
|
"not_invoiced": "Ej fakturerat",
|
||||||
|
"open_draft": "Öppet utkast",
|
||||||
|
"open_vehicles": "Öppna fordon",
|
||||||
|
"price_overrides": "Prisöverskrivningar",
|
||||||
|
"pricing": "Priser",
|
||||||
|
"subscription_invoicing": "Abonnemangsfakturering",
|
||||||
|
"subtitle": "Översikt över kunddata, åtkomst, priser, fordon och abonnemangsfakturering.",
|
||||||
|
"vehicles_load_failed": "Fordon kunde inte läsas in.",
|
||||||
|
"workspace_shortcuts": "Arbetsyta"
|
||||||
|
},
|
||||||
"permissions": "Tilladelser",
|
"permissions": "Tilladelser",
|
||||||
"user_data": "Brugerdata",
|
"user_data": "Brugerdata",
|
||||||
"user_id": "Användar-@.upper:{'words.generated.id'}",
|
"user_id": "Användar-@.upper:{'words.generated.id'}",
|
||||||
@@ -5945,6 +6215,9 @@
|
|||||||
"title": "@.capitalize:{'words.generated.avdelningspriser'}",
|
"title": "@.capitalize:{'words.generated.avdelningspriser'}",
|
||||||
"save": "@.capitalize:{'words.generated.spara'} @:{'words.generated.priser'}",
|
"save": "@.capitalize:{'words.generated.spara'} @:{'words.generated.priser'}",
|
||||||
"saved": "Priserna @:{'words.generated.har'} @:{'words.generated.sparats'}.",
|
"saved": "Priserna @:{'words.generated.har'} @:{'words.generated.sparats'}.",
|
||||||
|
"autosave_saving": "Sparar priser...",
|
||||||
|
"autosave_pending": "Prisandringar vantar.",
|
||||||
|
"autosave_saved": "Alla prisandringar har sparats.",
|
||||||
"product": "Produkt",
|
"product": "Produkt",
|
||||||
"price": "Avdelningspris",
|
"price": "Avdelningspris",
|
||||||
"price_required": "@.capitalize:{'words.generated.alla'} @:{'words.generated.produkter'} @:{'words.generated.maste'} ha ett icke-negativt numeriskt pris.",
|
"price_required": "@.capitalize:{'words.generated.alla'} @:{'words.generated.produkter'} @:{'words.generated.maste'} ha ett icke-negativt numeriskt pris.",
|
||||||
@@ -5959,6 +6232,15 @@
|
|||||||
"edit_title": "@:{'words.generated.redigera'} @:{'words.generated.medarbetare'}",
|
"edit_title": "@:{'words.generated.redigera'} @:{'words.generated.medarbetare'}",
|
||||||
"name": "Namn",
|
"name": "Namn",
|
||||||
"email": "E-post",
|
"email": "E-post",
|
||||||
|
"user_id": "Användar-ID",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"phone_validation": "Telefonnumret måste vara 4-15 siffror.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Danmark",
|
||||||
|
"sweden": "Sverige",
|
||||||
|
"norway": "Norge",
|
||||||
|
"finland": "Finland"
|
||||||
|
},
|
||||||
"password": "@.capitalize:{'words.generated.losenord'}",
|
"password": "@.capitalize:{'words.generated.losenord'}",
|
||||||
"password_create_help": "@.capitalize:{'words.generated.kravs'} @:{'words.generated.for'} nya @:{'words.generated.medarbetare'}. Använd @:{'words.generated.minst'} 8 tecken.",
|
"password_create_help": "@.capitalize:{'words.generated.kravs'} @:{'words.generated.for'} nya @:{'words.generated.medarbetare'}. Använd @:{'words.generated.minst'} 8 tecken.",
|
||||||
"password_update_help": "Lämna tomt @:{'words.generated.for'} att behålla nuvarande @:{'words.generated.losenord'}.",
|
"password_update_help": "Lämna tomt @:{'words.generated.for'} att behålla nuvarande @:{'words.generated.losenord'}.",
|
||||||
@@ -5977,7 +6259,13 @@
|
|||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"edit": "@:{'words.generated.redigera'}",
|
"edit": "@:{'words.generated.redigera'}",
|
||||||
"deactivate": "@.capitalize:{'words.generated.inaktivera'}"
|
"deactivate": "@.capitalize:{'words.generated.inaktivera'}",
|
||||||
|
"login_link": "QR-inloggning",
|
||||||
|
"login_link_title": "Inloggningslänk för medarbetare",
|
||||||
|
"login_link_qr_alt": "QR-kod för inloggningslänk",
|
||||||
|
"login_link_url": "Inloggningslänk",
|
||||||
|
"copy_login_link": "Kopiera länk",
|
||||||
|
"login_link_copied": "Länken har kopierats."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -6007,7 +6295,132 @@
|
|||||||
"save_prices": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.spara'} @:{'words.generated.avdelningspriser'}.",
|
"save_prices": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.spara'} @:{'words.generated.avdelningspriser'}.",
|
||||||
"load_employees": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.ladda'} @:{'words.generated.medarbetare'}.",
|
"load_employees": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.ladda'} @:{'words.generated.medarbetare'}.",
|
||||||
"save_employee": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.spara'} @:{'words.generated.medarbetaren'}.",
|
"save_employee": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.spara'} @:{'words.generated.medarbetaren'}.",
|
||||||
"deactivate_employee": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.inaktivera'} @:{'words.generated.medarbetaren'}."
|
"deactivate_employee": "@.capitalize:{'words.generated.kunde'} @:{'words.generated.inte'} @:{'words.generated.inaktivera'} @:{'words.generated.medarbetaren'}.",
|
||||||
|
"generate_employee_login_link": "Kunde inte skapa inloggningslänk för medarbetaren."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Rollbehörigheter",
|
||||||
|
"tooltip": "Visa rollbehörigheter",
|
||||||
|
"intro": "Roller samlar de exakta funktioner som tilldelas en medarbetare när rollen sparas.",
|
||||||
|
"scope": "Alla funktioner är begränsade till de avdelningar som väljs för medarbetaren.",
|
||||||
|
"selected": "Vald",
|
||||||
|
"close": "Stäng",
|
||||||
|
"unavailable": "Behörighetsdetaljer är inte tillgängliga för den här rollen förrän rollkatalogen har uppdaterats.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Konto",
|
||||||
|
"orders": "Order",
|
||||||
|
"bookings": "Bokningar",
|
||||||
|
"time_bookings": "Tidsbokningar",
|
||||||
|
"reports": "Rapporter",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Övrigt"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Logga in",
|
||||||
|
"description": "Ger medarbetaren möjlighet att logga in i Pleno."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "Se egna behörigheter",
|
||||||
|
"description": "Visar medarbetaren vilka åtkomsträttigheter kontot har."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "Se order",
|
||||||
|
"description": "Kan öppna orderlistor och orderdetaljer för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Skapa order",
|
||||||
|
"description": "Kan skapa nya order för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Redigera order",
|
||||||
|
"description": "Kan ändra orderdetaljer för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Ta bort order",
|
||||||
|
"description": "Kan ta bort order i tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "Se orderrader",
|
||||||
|
"description": "Kan se produkter, tjänster och priser på order."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Skapa orderrader",
|
||||||
|
"description": "Kan lägga till produkter och tjänster på order."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Redigera orderrader",
|
||||||
|
"description": "Kan ändra produkter, antal och priser på order."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Ta bort orderrader",
|
||||||
|
"description": "Kan ta bort produkter och tjänster från order."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Debitera order",
|
||||||
|
"description": "Kan ta betalt eller debitera en betalningsreservation för en order."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "Se avdelningsbokningar",
|
||||||
|
"description": "Kan se bokningar för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "Se egna bokningar",
|
||||||
|
"description": "Kan se bokningsantal och bokningar kopplade till medarbetarkontot."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Redigera bokningar",
|
||||||
|
"description": "Kan ändra tillåtna bokningsdetaljer."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Skapa bokningar",
|
||||||
|
"description": "Kan skapa nya bokningar för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Slutför bokningar",
|
||||||
|
"description": "Kan markera bokningar som slutförda."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Skicka bokningsbekräftelser igen",
|
||||||
|
"description": "Kan skicka bokningsbekräftelser och slutförandemejl igen."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "Se tidsbokningar",
|
||||||
|
"description": "Kan granska tidsbokningsposter för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Skapa tidsbokningar",
|
||||||
|
"description": "Kan lägga till nya tidsbokningsposter."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Redigera tidsbokningar",
|
||||||
|
"description": "Kan redigera tidsbokningsposter när backend-rutten stöder det."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "Se orderstatistik",
|
||||||
|
"description": "Kan öppna orderstatistik för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "Se bokningsstatistik",
|
||||||
|
"description": "Kan öppna bokningsstatistik för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Öppna begränsat backoffice",
|
||||||
|
"description": "Kan öppna det begränsade backofficeområdet."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Hantera avdelningspriser",
|
||||||
|
"description": "Kan redigera uttryckliga priser för tilldelade avdelningar."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Hantera medarbetaråtkomst",
|
||||||
|
"description": "Kan skapa, redigera och inaktivera begränsade backofficemedarbetare."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Extra funktion",
|
||||||
|
"description": "Den här funktionen kommer från rollkatalogen men är ännu inte beskriven i gränssnittet."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,7 @@
|
|||||||
"company_phone": "Virksomhedstelefon",
|
"company_phone": "Virksomhedstelefon",
|
||||||
"contact_email": "@.capitalize:{'terms.glossary.kontakt'}-@:{'terms.glossary.e'}-@:{'terms.glossary.mail'}",
|
"contact_email": "@.capitalize:{'terms.glossary.kontakt'}-@:{'terms.glossary.e'}-@:{'terms.glossary.mail'}",
|
||||||
"contact_phone": "Kontakttelefon",
|
"contact_phone": "Kontakttelefon",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'terms.glossary.oprettet'} @:{'terms.glossary.af'}",
|
"created_by": "@.capitalize:{'terms.glossary.oprettet'} @:{'terms.glossary.af'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'terms.replication.article.host_mention'} @:{'terms.glossary.indtastede'} @:{'terms.glossary.nummerplade'} @:{'terms.glossary.tilhører'} @:{'terms.replication.host_definite_suffix'} @:{'terms.glossary.anden'} @:{'terms.glossary.gemt'} @:{'terms.glossary.kunde'}. @.capitalize:{'terms.glossary.vælg'} @:{'terms.glossary.om'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.valgte'} @:{'terms.glossary.kunde'} @:{'terms.glossary.skal'} @:{'terms.glossary.beholdes'}, @:{'terms.glossary.eller'} @:{'terms.glossary.om'} @:{'terms.glossary.der'} @:{'terms.glossary.skal'} @:{'terms.glossary.skiftes'} @:{'terms.glossary.til'} @:{'terms.glossary.køretøjets'} @:{'terms.glossary.kunde'}.",
|
"help_text": "@.capitalize:{'terms.replication.article.host_mention'} @:{'terms.glossary.indtastede'} @:{'terms.glossary.nummerplade'} @:{'terms.glossary.tilhører'} @:{'terms.replication.host_definite_suffix'} @:{'terms.glossary.anden'} @:{'terms.glossary.gemt'} @:{'terms.glossary.kunde'}. @.capitalize:{'terms.glossary.vælg'} @:{'terms.glossary.om'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.valgte'} @:{'terms.glossary.kunde'} @:{'terms.glossary.skal'} @:{'terms.glossary.beholdes'}, @:{'terms.glossary.eller'} @:{'terms.glossary.om'} @:{'terms.glossary.der'} @:{'terms.glossary.skal'} @:{'terms.glossary.skiftes'} @:{'terms.glossary.til'} @:{'terms.glossary.køretøjets'} @:{'terms.glossary.kunde'}.",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"benefit_portal": "@:{'terms.glossary.kundeportal'}",
|
"benefit_portal": "@:{'terms.glossary.kundeportal'}",
|
||||||
"benefit_portal_desc": "@:{'terms.glossary.benyt'} kundeportalen @:{'terms.glossary.til'} @:{'terms.glossary.at'} @:{'terms.glossary.tilføje'} @:{'terms.glossary.køretøjer'}, @:{'terms.glossary.booke'} @:{'terms.glossary.vaske'}, downloade @:{'terms.glossary.fakturaer'}, @:{'terms.glossary.vaskecertifikater'} @:{'terms.glossary.og'} @:{'terms.glossary.information'} @:{'terms.glossary.for'} @:{'terms.glossary.historiske'} @:{'terms.glossary.vaske'}",
|
"benefit_portal_desc": "@:{'terms.glossary.benyt'} kundeportalen @:{'terms.glossary.til'} @:{'terms.glossary.at'} @:{'terms.glossary.tilføje'} @:{'terms.glossary.køretøjer'}, @:{'terms.glossary.booke'} @:{'terms.glossary.vaske'}, downloade @:{'terms.glossary.fakturaer'}, @:{'terms.glossary.vaskecertifikater'} @:{'terms.glossary.og'} @:{'terms.glossary.information'} @:{'terms.glossary.for'} @:{'terms.glossary.historiske'} @:{'terms.glossary.vaske'}",
|
||||||
"benefit_wash_desc": "@:{'terms.glossary.benyt'} @:{'terms.glossary.vores'} @:{'terms.glossary.nationale'} @:{'terms.glossary.netværk'} @:{'terms.glossary.af'} @:{'terms.glossary.vaskehaller'} @:{'terms.glossary.til'} @:{'terms.glossary.konkurrencedygtige'} @:{'terms.glossary.priser'}",
|
"benefit_wash_desc": "@:{'terms.glossary.benyt'} @:{'terms.glossary.vores'} @:{'terms.glossary.nationale'} @:{'terms.glossary.netværk'} @:{'terms.glossary.af'} @:{'terms.glossary.vaskehaller'} @:{'terms.glossary.til'} @:{'terms.glossary.konkurrencedygtige'} @:{'terms.glossary.priser'}",
|
||||||
|
"ean_label": "@:{'phrases.compat.admin.pos.ean'}",
|
||||||
"email_label": "@:{'terms.glossary.firma'} @:{'terms.glossary.email'}",
|
"email_label": "@:{'terms.glossary.firma'} @:{'terms.glossary.email'}",
|
||||||
"intro": "@.capitalize:{'terms.glossary.som'} @:{'terms.glossary.kunde'} @:{'terms.glossary.hos'} @:{'terms.glossary.truck'} @:{'terms.glossary.wash'} @:{'terms.glossary.far'} @:{'terms.glossary.du'} @:{'terms.glossary.adgang'} @:{'terms.glossary.til'}:",
|
"intro": "@.capitalize:{'terms.glossary.som'} @:{'terms.glossary.kunde'} @:{'terms.glossary.hos'} @:{'terms.glossary.truck'} @:{'terms.glossary.wash'} @:{'terms.glossary.far'} @:{'terms.glossary.du'} @:{'terms.glossary.adgang'} @:{'terms.glossary.til'}:",
|
||||||
"phone_label": "@:{'terms.glossary.firma'} @:{'terms.glossary.telefon'}",
|
"phone_label": "@:{'terms.glossary.firma'} @:{'terms.glossary.telefon'}",
|
||||||
|
|||||||
@@ -21,6 +21,13 @@
|
|||||||
"opening_hours": {
|
"opening_hours": {
|
||||||
"closed": "Stengt"
|
"closed": "Stengt"
|
||||||
},
|
},
|
||||||
|
"pricing": {
|
||||||
|
"custom_pricing_disabled": "Fallback aktiv",
|
||||||
|
"custom_pricing_enabled": "Kun egne priser",
|
||||||
|
"custom_pricing_missing_price": "Manglende afdelingspriser bliver 999999.",
|
||||||
|
"custom_pricing_only": "Ingen fallback-priser",
|
||||||
|
"effective_department_price": "Effektiv afdelingspris"
|
||||||
|
},
|
||||||
"search_placeholder": "@.capitalize:{'terms.glossary.søg'} @:{'terms.glossary.efter'} @:{'terms.glossary.afdelingsnavn'}",
|
"search_placeholder": "@.capitalize:{'terms.glossary.søg'} @:{'terms.glossary.efter'} @:{'terms.glossary.afdelingsnavn'}",
|
||||||
"select_department": "@.capitalize:{'terms.glossary.vælg'} @:{'terms.replication.host_definite_suffix'} @:{'terms.glossary.afdeling'}",
|
"select_department": "@.capitalize:{'terms.glossary.vælg'} @:{'terms.replication.host_definite_suffix'} @:{'terms.glossary.afdeling'}",
|
||||||
"subtitle": "@.capitalize:{'terms.glossary.administrer'} @:{'terms.glossary.afdelinger'}",
|
"subtitle": "@.capitalize:{'terms.glossary.administrer'} @:{'terms.glossary.afdelinger'}",
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Rapporter fejl",
|
"button": "Rapporter fejl",
|
||||||
"title": "Rapporter fejl",
|
"title": "Rapporter fejl",
|
||||||
"subtitle": "Send det aktuelle skærmbillede og de seneste fejldetaljer til support.",
|
"subtitle": "Send de seneste fejldetaljer til support. Et skærmbillede vedhæftes, når det er muligt.",
|
||||||
"before_error": "Hvad lavede du, før fejlen opstod?",
|
"before_error": "Hvad lavede du, før fejlen opstod?",
|
||||||
"expected": "Hvad forventede du, der ville ske?",
|
"expected": "Hvad forventede du, der ville ske?",
|
||||||
"actual": "Hvad skete der faktisk?",
|
"actual": "Hvad skete der faktisk?",
|
||||||
"before_error_placeholder": "Beskriv handlingen, du var i gang med, for eksempel at åbne ordrer eller vælge en kunde.",
|
"before_error_placeholder": "Beskriv handlingen, du var i gang med, for eksempel at åbne ordrer eller vælge en kunde.",
|
||||||
"expected_placeholder": "Beskriv det resultat, du forventede at se.",
|
"expected_placeholder": "Beskriv det resultat, du forventede at se.",
|
||||||
"actual_placeholder": "Beskriv, hvad du så i stedet, inklusive eventuel fejltekst.",
|
"actual_placeholder": "Beskriv, hvad du så i stedet, inklusive eventuel fejltekst.",
|
||||||
"consent": "Jeg accepterer, at det aktuelle app-skærmbillede, de seneste request-fejl, Vue-fejl, browseroplysninger og mine svar indsamles til fejlfinding.",
|
"consent": "Jeg accepterer, at de seneste request-fejl, Vue-fejl, browseroplysninger, mine svar og et app-skærmbillede, når det er muligt, indsamles til fejlfinding.",
|
||||||
"submit": "Send rapport",
|
"submit": "Send rapport",
|
||||||
"submitted": "Fejlrapporten er sendt.",
|
"submitted": "Fejlrapporten er sendt.",
|
||||||
"capture_failed": "Skærmbilledet kunne ikke oprettes. Prøv igen.",
|
"capture_failed": "Skærmbilledet kunne ikke oprettes. Rapporten sendes uden skærmbillede.",
|
||||||
"submit_failed": "Fejlrapporten kunne ikke sendes.",
|
"submit_failed": "Fejlrapporten kunne ikke sendes.",
|
||||||
"required": "Alle felter og accept af dataindsamling er påkrævet.",
|
"required": "Alle felter og accept af dataindsamling er påkrævet.",
|
||||||
"page_subtitle": "Rapporterede skærme, request-fejl og Vue-fejl",
|
"page_subtitle": "Rapporterede skærme, request-fejl og Vue-fejl",
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compat": {
|
||||||
|
"invoicing_period": {
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Fakturer sammen",
|
||||||
|
"split_by_month": "Opdel efter måned",
|
||||||
|
"split_error_title": "Månedsopdeling mislykkedes",
|
||||||
|
"split_success_text": "Behandlede {processed} fakturasamlinger. Opdelte {changed} og sprang {skipped} over.",
|
||||||
|
"split_success_title": "Månedsopdeling fuldført",
|
||||||
|
"text": "Du er ved at fakturere ordrer fra flere måneder sammen ({months}). Skal de i stedet opdeles efter måned?",
|
||||||
|
"title": "Ordrer fra flere måneder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,6 +140,7 @@
|
|||||||
"departments": {
|
"departments": {
|
||||||
"columns": {
|
"columns": {
|
||||||
"archived": "Arkiveret",
|
"archived": "Arkiveret",
|
||||||
|
"custom_pricing_only": "Ingen fallback-priser",
|
||||||
"dimension": "Dimension",
|
"dimension": "Dimension",
|
||||||
"economic_department": "@.upper:{'terms.glossary.e'}-@:{'terms.glossary.conomic'} @:{'terms.glossary.afdeling'}",
|
"economic_department": "@.upper:{'terms.glossary.e'}-@:{'terms.glossary.conomic'} @:{'terms.glossary.afdeling'}",
|
||||||
"latitude": "@:{'phrases.compat.departments.form.latitude'}",
|
"latitude": "@:{'phrases.compat.departments.form.latitude'}",
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"compat": {
|
||||||
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Adgangsomrade",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Ryd gruppe",
|
||||||
|
"reload": "Genindlaes",
|
||||||
|
"retry": "Prov igen",
|
||||||
|
"select_all": "Vaelg gruppe"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Aktiveret",
|
||||||
|
"permission": "Tilladelse",
|
||||||
|
"route": "Rute",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Giver adgang til {name}.",
|
||||||
|
"empty": "Der er ingen tilladelser tilgaengelige.",
|
||||||
|
"empty_group": "Denne gruppe har ingen tilladelser.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Rolletilladelser kunne ikke indlaeses.",
|
||||||
|
"update": "En eller flere tilladelser kunne ikke opdateres. Rollens aktuelle tilladelser blev genindlaest."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} matcher",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Sidetype- og afdelingsadgang.",
|
||||||
|
"label": "Adgang"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Tilladelser til bookinger, tidsbookinger og planlaegning.",
|
||||||
|
"label": "Bookinger"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Tilladelser til kunder, brugere, medarbejdere, roller og login.",
|
||||||
|
"label": "Kunder og brugere"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Afdelingsdrift, baner, relaer, notifikationer, rapporter og mal.",
|
||||||
|
"label": "Afdelinger og drift"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Tilladelser til fakturaer, faktureringsperioder, opkraevning og e-conomic.",
|
||||||
|
"label": "Fakturering og e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Tilladelser til ordrer, POS, betalinger og ordrelinjer.",
|
||||||
|
"label": "Ordrer og POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Tilladelser, der ikke matcher en anden gruppe.",
|
||||||
|
"label": "Andet"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Tilladelser til produkter, priser, rabatter og valuta.",
|
||||||
|
"label": "Produkter og priser"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Tilladelser til selvbetjening, vask, scannere, gateways og edge-udstyr.",
|
||||||
|
"label": "Selvbetjening og edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, konfiguration, release, replikering og integrationer.",
|
||||||
|
"label": "System og konfiguration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "Der er ingen beskrivelse.",
|
||||||
|
"no_search_results": "Ingen tilladelser matcher sogningen.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Giver adgang til administrationssider.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Giver adgang til superbrugersider.",
|
||||||
|
"label": "Superbruger"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Giver adgang til brugersider.",
|
||||||
|
"label": "Bruger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Sog i tilladelser",
|
||||||
|
"search_placeholder": "Sog pa label, nogle, beskrivelse, rute eller metode",
|
||||||
|
"selected_summary": "{selected} af {total} valgt",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Deaktiveret",
|
||||||
|
"enabled": "Aktiveret"
|
||||||
|
},
|
||||||
|
"subtitle": "Rolletilladelser",
|
||||||
|
"title": "Tilladelser for rolle {id}",
|
||||||
|
"title_with_name": "Tilladelser for {name}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,31 @@
|
|||||||
"transaction_history": "@:{'phrases.compat.superuser.nav.orders'}"
|
"transaction_history": "@:{'phrases.compat.superuser.nav.orders'}"
|
||||||
},
|
},
|
||||||
"other": "@:{'phrases.compat.global.other'}",
|
"other": "@:{'phrases.compat.global.other'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Aktive køretøjer",
|
||||||
|
"attributes": "Attributter",
|
||||||
|
"connected": "Forbundet",
|
||||||
|
"customer_management": "Kundestyring",
|
||||||
|
"customer_number_required": "Et kundenummer er påkrævet før kundeindstillinger kan administreres.",
|
||||||
|
"customer_rules": "Kunderegler",
|
||||||
|
"effective_access": "Effektiv adgang",
|
||||||
|
"loaded": "Indlæst",
|
||||||
|
"load_failed": "Brugerdata kunne ikke indlæses",
|
||||||
|
"loading_user": "Indlæser bruger",
|
||||||
|
"management_hub": "Administrationshub",
|
||||||
|
"missing": "Mangler",
|
||||||
|
"more_permissions": "+{count} flere tilladelser",
|
||||||
|
"no_subscription_transactions": "Der er ikke registreret vaskeabonnementstransaktioner for kunden.",
|
||||||
|
"not_invoiced": "Ikke faktureret",
|
||||||
|
"open_draft": "Åben kladde",
|
||||||
|
"open_vehicles": "Åbn køretøjer",
|
||||||
|
"price_overrides": "Prisoverstyringer",
|
||||||
|
"pricing": "Priser",
|
||||||
|
"subscription_invoicing": "Abonnementsfakturering",
|
||||||
|
"subtitle": "Overblik over kundedata, adgang, priser, køretøjer og abonnementsfakturering.",
|
||||||
|
"vehicles_load_failed": "Køretøjer kunne ikke indlæses.",
|
||||||
|
"workspace_shortcuts": "Arbejdsgange"
|
||||||
|
},
|
||||||
"permissions": "@.capitalize:{'terms.glossary.tilladelser'}",
|
"permissions": "@.capitalize:{'terms.glossary.tilladelser'}",
|
||||||
"user_data": "@.capitalize:{'terms.glossary.brugerdata'}",
|
"user_data": "@.capitalize:{'terms.glossary.brugerdata'}",
|
||||||
"user_id": "@.capitalize:{'terms.glossary.bruger'} @.upper:{'terms.glossary.id'}",
|
"user_id": "@.capitalize:{'terms.glossary.bruger'} @.upper:{'terms.glossary.id'}",
|
||||||
|
|||||||
@@ -2,6 +2,20 @@
|
|||||||
"compat": {
|
"compat": {
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"add": "@:{'terms.glossary.tilføj'} @:{'terms.glossary.køretøj'}",
|
"add": "@:{'terms.glossary.tilføj'} @:{'terms.glossary.køretøj'}",
|
||||||
|
"add_modal": {
|
||||||
|
"customer_label": "Kunde",
|
||||||
|
"customer_placeholder": "Søg efter kundenavn eller kundenummer",
|
||||||
|
"error": "Køretøjet kunne ikke tilføjes.",
|
||||||
|
"no_customer_results": "Ingen kunder fundet",
|
||||||
|
"reference_placeholder": "Valgfri reference",
|
||||||
|
"registration_placeholder": "Registreringsnummer",
|
||||||
|
"selected_customer": "Valgt kunde",
|
||||||
|
"submit": "Tilføj køretøj",
|
||||||
|
"title": "Tilføj køretøj",
|
||||||
|
"type_load_error": "Køretøjstyper kunne ikke indlæses.",
|
||||||
|
"type_placeholder": "Vælg køretøjstype",
|
||||||
|
"validation_error": "Vælg kunde, registreringsnummer og køretøjstype."
|
||||||
|
},
|
||||||
"brand": "@:{'phrases.compat.admin.pos.make'}",
|
"brand": "@:{'phrases.compat.admin.pos.make'}",
|
||||||
"color": "Farve",
|
"color": "Farve",
|
||||||
"delete_vehicle": "@:{'phrases.compat.vehicles.delete'}",
|
"delete_vehicle": "@:{'phrases.compat.vehicles.delete'}",
|
||||||
|
|||||||
@@ -28,13 +28,16 @@
|
|||||||
"title": "@:{'phrases.compat.limited_backoffice.forbidden.title'}",
|
"title": "@:{'phrases.compat.limited_backoffice.forbidden.title'}",
|
||||||
"department": "@:{'phrases.compat.limited_backoffice.forbidden.department'}"
|
"department": "@:{'phrases.compat.limited_backoffice.forbidden.department'}"
|
||||||
},
|
},
|
||||||
"prices": {
|
"prices": {
|
||||||
"title": "@:{'phrases.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'phrases.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'phrases.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'phrases.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'phrases.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'phrases.compat.limited_backoffice.prices.saved'}",
|
||||||
"product": "@:{'phrases.compat.limited_backoffice.prices.product'}",
|
"autosave_saving": "@:{'phrases.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
"price": "@:{'phrases.compat.limited_backoffice.prices.price'}",
|
"autosave_pending": "@:{'phrases.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
"price_required": "@:{'phrases.compat.limited_backoffice.prices.price_required'}",
|
"autosave_saved": "@:{'phrases.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
|
"product": "@:{'phrases.compat.limited_backoffice.prices.product'}",
|
||||||
|
"price": "@:{'phrases.compat.limited_backoffice.prices.price'}",
|
||||||
|
"price_required": "@:{'phrases.compat.limited_backoffice.prices.price_required'}",
|
||||||
"setup_required_title": "@:{'phrases.compat.limited_backoffice.prices.setup_required_title'}",
|
"setup_required_title": "@:{'phrases.compat.limited_backoffice.prices.setup_required_title'}",
|
||||||
"setup_required_message": "@:{'phrases.compat.limited_backoffice.prices.setup_required_message'}",
|
"setup_required_message": "@:{'phrases.compat.limited_backoffice.prices.setup_required_message'}",
|
||||||
"no_products": "@:{'phrases.compat.limited_backoffice.prices.no_products'}"
|
"no_products": "@:{'phrases.compat.limited_backoffice.prices.no_products'}"
|
||||||
@@ -46,6 +49,15 @@
|
|||||||
"edit_title": "@:{'phrases.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'phrases.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'phrases.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'phrases.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'phrases.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'phrases.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'phrases.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'phrases.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'phrases.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'phrases.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'phrases.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'phrases.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'phrases.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'phrases.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'phrases.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'phrases.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'phrases.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'phrases.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'phrases.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -64,7 +76,13 @@
|
|||||||
"active": "@:{'phrases.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'phrases.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'phrases.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'phrases.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'phrases.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'phrases.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'phrases.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'phrases.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'phrases.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'phrases.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'phrases.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'phrases.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'phrases.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'phrases.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -94,7 +112,132 @@
|
|||||||
"save_prices": "@:{'phrases.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'phrases.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'phrases.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'phrases.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'phrases.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'phrases.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'phrases.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'phrases.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'phrases.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'phrases.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'phrases.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'phrases.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'phrases.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'phrases.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'phrases.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'phrases.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -141,6 +284,9 @@
|
|||||||
"title": "@.capitalize:{'terms.glossary.afdelingspriser'}",
|
"title": "@.capitalize:{'terms.glossary.afdelingspriser'}",
|
||||||
"save": "@:{'terms.glossary.gem'} @:{'terms.glossary.priser'}",
|
"save": "@:{'terms.glossary.gem'} @:{'terms.glossary.priser'}",
|
||||||
"saved": "Priserne @:{'terms.glossary.er'} @:{'terms.glossary.gemt'}.",
|
"saved": "Priserne @:{'terms.glossary.er'} @:{'terms.glossary.gemt'}.",
|
||||||
|
"autosave_saving": "Gemmer priser...",
|
||||||
|
"autosave_pending": "Prisændringer afventer.",
|
||||||
|
"autosave_saved": "Alle prisændringer er gemt.",
|
||||||
"product": "Produkt",
|
"product": "Produkt",
|
||||||
"price": "Afdelingspris",
|
"price": "Afdelingspris",
|
||||||
"price_required": "@.capitalize:{'terms.glossary.alle'} @:{'terms.glossary.produkter'} @:{'terms.glossary.skal'} have @:{'terms.replication.host_definite_suffix'} @:{'terms.glossary.ikke'}-negativ numerisk pris.",
|
"price_required": "@.capitalize:{'terms.glossary.alle'} @:{'terms.glossary.produkter'} @:{'terms.glossary.skal'} have @:{'terms.replication.host_definite_suffix'} @:{'terms.glossary.ikke'}-negativ numerisk pris.",
|
||||||
@@ -155,6 +301,15 @@
|
|||||||
"edit_title": "@:{'terms.glossary.rediger'} @:{'terms.glossary.medarbejder'}",
|
"edit_title": "@:{'terms.glossary.rediger'} @:{'terms.glossary.medarbejder'}",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"user_id": "Bruger-id",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"phone_validation": "Telefonnummeret skal være 4-15 cifre.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Danmark",
|
||||||
|
"sweden": "Sverige",
|
||||||
|
"norway": "Norge",
|
||||||
|
"finland": "Finland"
|
||||||
|
},
|
||||||
"password": "@.capitalize:{'terms.glossary.adgangskode'}",
|
"password": "@.capitalize:{'terms.glossary.adgangskode'}",
|
||||||
"password_create_help": "@.capitalize:{'terms.glossary.pakrævet'} @:{'terms.glossary.for'} nye @:{'terms.glossary.medarbejdere'}. Brug @:{'terms.glossary.mindst'} 8 tegn.",
|
"password_create_help": "@.capitalize:{'terms.glossary.pakrævet'} @:{'terms.glossary.for'} nye @:{'terms.glossary.medarbejdere'}. Brug @:{'terms.glossary.mindst'} 8 tegn.",
|
||||||
"password_update_help": "Lad feltet være tomt @:{'terms.glossary.for'} at beholde den nuværende @:{'terms.glossary.adgangskode'}.",
|
"password_update_help": "Lad feltet være tomt @:{'terms.glossary.for'} at beholde den nuværende @:{'terms.glossary.adgangskode'}.",
|
||||||
@@ -173,7 +328,13 @@
|
|||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"edit": "@:{'terms.glossary.rediger'}",
|
"edit": "@:{'terms.glossary.rediger'}",
|
||||||
"deactivate": "Deaktiver"
|
"deactivate": "Deaktiver",
|
||||||
|
"login_link": "QR-login",
|
||||||
|
"login_link_title": "Medarbejderloginlink",
|
||||||
|
"login_link_qr_alt": "QR-kode-loginlink",
|
||||||
|
"login_link_url": "Loginlink",
|
||||||
|
"copy_login_link": "Kopiér link",
|
||||||
|
"login_link_copied": "Linket er kopieret."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -203,7 +364,132 @@
|
|||||||
"save_prices": "@.capitalize:{'terms.glossary.afdelingspriser'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} @:{'terms.glossary.gemmes'}.",
|
"save_prices": "@.capitalize:{'terms.glossary.afdelingspriser'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} @:{'terms.glossary.gemmes'}.",
|
||||||
"load_employees": "@.capitalize:{'terms.glossary.medarbejdere'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} @:{'terms.glossary.indlæses'}.",
|
"load_employees": "@.capitalize:{'terms.glossary.medarbejdere'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} @:{'terms.glossary.indlæses'}.",
|
||||||
"save_employee": "@.capitalize:{'terms.glossary.medarbejderen'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} @:{'terms.glossary.gemmes'}.",
|
"save_employee": "@.capitalize:{'terms.glossary.medarbejderen'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} @:{'terms.glossary.gemmes'}.",
|
||||||
"deactivate_employee": "@.capitalize:{'terms.glossary.medarbejderen'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} deaktiveres."
|
"deactivate_employee": "@.capitalize:{'terms.glossary.medarbejderen'} @:{'terms.glossary.kunne'} @:{'terms.glossary.ikke'} deaktiveres.",
|
||||||
|
"generate_employee_login_link": "Medarbejderens loginlink kunne ikke oprettes."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Rolletilladelser",
|
||||||
|
"tooltip": "Vis rolletilladelser",
|
||||||
|
"intro": "Roller samler de præcise funktioner, der tildeles en medarbejder, når rollen gemmes.",
|
||||||
|
"scope": "Alle funktioner er begrænset til de afdelinger, der vælges for medarbejderen.",
|
||||||
|
"selected": "Valgt",
|
||||||
|
"close": "Luk",
|
||||||
|
"unavailable": "Tilladelsesdetaljer er ikke tilgængelige for denne rolle, før rollekataloget er opdateret.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Konto",
|
||||||
|
"orders": "Ordrer",
|
||||||
|
"bookings": "Bookinger",
|
||||||
|
"time_bookings": "Tidsbookinger",
|
||||||
|
"reports": "Rapporter",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Øvrigt"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Log ind",
|
||||||
|
"description": "Giver medarbejderen adgang til at logge ind i Pleno."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "Se egne tilladelser",
|
||||||
|
"description": "Viser medarbejderen, hvilke adgangsrettigheder kontoen har."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "Se ordrer",
|
||||||
|
"description": "Kan åbne ordrelister og ordredetaljer for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Opret ordrer",
|
||||||
|
"description": "Kan oprette nye ordrer for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Rediger ordrer",
|
||||||
|
"description": "Kan ændre ordredetaljer for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Slet ordrer",
|
||||||
|
"description": "Kan slette ordrer i tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "Se ordrelinjer",
|
||||||
|
"description": "Kan se produkter, ydelser og priser på ordrer."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Opret ordrelinjer",
|
||||||
|
"description": "Kan tilføje produkter og ydelser til ordrer."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Rediger ordrelinjer",
|
||||||
|
"description": "Kan ændre produkter, antal og priser på ordrer."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Slet ordrelinjer",
|
||||||
|
"description": "Kan fjerne produkter og ydelser fra ordrer."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Opkræv ordrer",
|
||||||
|
"description": "Kan tage betaling eller opkræve en betalingsreservation for en ordre."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "Se afdelingsbookinger",
|
||||||
|
"description": "Kan se bookinger for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "Se egne bookinger",
|
||||||
|
"description": "Kan se bookingantal og bookinger knyttet til medarbejderkontoen."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Rediger bookinger",
|
||||||
|
"description": "Kan ændre tilladte bookingdetaljer."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Opret bookinger",
|
||||||
|
"description": "Kan oprette nye bookinger for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Afslut bookinger",
|
||||||
|
"description": "Kan markere bookinger som afsluttet."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Gensend bookingbekræftelser",
|
||||||
|
"description": "Kan gensende bookingbekræftelser og afslutningsmails."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "Se tidsbookinger",
|
||||||
|
"description": "Kan gennemse tidsbookingposter for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Opret tidsbookinger",
|
||||||
|
"description": "Kan oprette nye tidsbookingposter."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Rediger tidsbookinger",
|
||||||
|
"description": "Kan redigere tidsbookingposter, når backend-ruten understøtter det."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "Se ordrestatistik",
|
||||||
|
"description": "Kan åbne statistikvisninger for ordrer i tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "Se bookingstatistik",
|
||||||
|
"description": "Kan åbne statistikvisninger for bookinger i tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Åbn begrænset backoffice",
|
||||||
|
"description": "Kan tilgå det begrænsede backofficeområde."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Administrer afdelingspriser",
|
||||||
|
"description": "Kan redigere eksplicitte priser for tildelte afdelinger."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Administrer medarbejderadgang",
|
||||||
|
"description": "Kan oprette, redigere og deaktivere begrænsede backofficemedarbejdere."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Ekstra funktion",
|
||||||
|
"description": "Denne funktion kommer fra rollekataloget, men er endnu ikke beskrevet i grænsefladen."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,7 @@
|
|||||||
"company_phone": "Firmentelefon",
|
"company_phone": "Firmentelefon",
|
||||||
"contact_email": "@:{'terms.glossary.kontakt'}-@.upper:{'terms.glossary.e'}-@:{'terms.glossary.mail'}",
|
"contact_email": "@:{'terms.glossary.kontakt'}-@.upper:{'terms.glossary.e'}-@:{'terms.glossary.mail'}",
|
||||||
"contact_phone": "Kontakttelefonnummer",
|
"contact_phone": "Kontakttelefonnummer",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'terms.glossary.erstellt'} @:{'terms.glossary.von'}",
|
"created_by": "@.capitalize:{'terms.glossary.erstellt'} @:{'terms.glossary.von'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'terms.glossary.dieses'} @:{'terms.glossary.kennzeichen'} @:{'terms.glossary.gehoert'} @:{'terms.glossary.zu'} @:{'terms.glossary.einem'} @:{'terms.glossary.anderen'} @:{'terms.glossary.gespeicherten'} @:{'terms.glossary.kunden'}. @:{'terms.glossary.waehlen'} @.capitalize:{'terms.glossary.sie'}, @:{'terms.glossary.ob'} @:{'terms.glossary.der'} @:{'terms.glossary.ausgewaehlte'} @.capitalize:{'terms.glossary.kunde'} @:{'terms.glossary.beibehalten'} @:{'terms.glossary.oder'} @:{'terms.glossary.zum'} @:{'terms.glossary.fahrzeugkunden'} @:{'terms.glossary.gewechselt'} @:{'terms.glossary.werden'} @:{'terms.glossary.soll'}.",
|
"help_text": "@.capitalize:{'terms.glossary.dieses'} @:{'terms.glossary.kennzeichen'} @:{'terms.glossary.gehoert'} @:{'terms.glossary.zu'} @:{'terms.glossary.einem'} @:{'terms.glossary.anderen'} @:{'terms.glossary.gespeicherten'} @:{'terms.glossary.kunden'}. @:{'terms.glossary.waehlen'} @.capitalize:{'terms.glossary.sie'}, @:{'terms.glossary.ob'} @:{'terms.glossary.der'} @:{'terms.glossary.ausgewaehlte'} @.capitalize:{'terms.glossary.kunde'} @:{'terms.glossary.beibehalten'} @:{'terms.glossary.oder'} @:{'terms.glossary.zum'} @:{'terms.glossary.fahrzeugkunden'} @:{'terms.glossary.gewechselt'} @:{'terms.glossary.werden'} @:{'terms.glossary.soll'}.",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"benefit_portal": "@:{'terms.glossary.kundenportal'}",
|
"benefit_portal": "@:{'terms.glossary.kundenportal'}",
|
||||||
"benefit_portal_desc": "@.capitalize:{'terms.glossary.nutzen'} @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.das'} @:{'terms.glossary.kundenportal'}, @:{'terms.glossary.um'} @:{'terms.glossary.fahrzeuge'} hinzuzuf?@:{'terms.glossary.gen'}, @.upper:{'terms.glossary.w'}?@:{'terms.glossary.schen'} @:{'terms.glossary.zu'} @:{'terms.glossary.buchen'}, @:{'terms.glossary.rechnungen'} @:{'terms.glossary.und'} @:{'terms.glossary.waschzertifikate'} herunterzuladen @:{'terms.glossary.sowie'} @:{'terms.glossary.informationen'} @:{'terms.glossary.zu'} @:{'terms.glossary.fr'}?@:{'terms.glossary.heren'} @.upper:{'terms.glossary.w'}?@:{'terms.glossary.schen'} einzusehen",
|
"benefit_portal_desc": "@.capitalize:{'terms.glossary.nutzen'} @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.das'} @:{'terms.glossary.kundenportal'}, @:{'terms.glossary.um'} @:{'terms.glossary.fahrzeuge'} hinzuzuf?@:{'terms.glossary.gen'}, @.upper:{'terms.glossary.w'}?@:{'terms.glossary.schen'} @:{'terms.glossary.zu'} @:{'terms.glossary.buchen'}, @:{'terms.glossary.rechnungen'} @:{'terms.glossary.und'} @:{'terms.glossary.waschzertifikate'} herunterzuladen @:{'terms.glossary.sowie'} @:{'terms.glossary.informationen'} @:{'terms.glossary.zu'} @:{'terms.glossary.fr'}?@:{'terms.glossary.heren'} @.upper:{'terms.glossary.w'}?@:{'terms.glossary.schen'} einzusehen",
|
||||||
"benefit_wash_desc": "@.capitalize:{'terms.glossary.nutzen'} @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.unser'} @:{'terms.glossary.nationales'} @:{'terms.glossary.netz'} @:{'terms.glossary.von'} @:{'terms.glossary.waschhallen'} @:{'terms.glossary.zu'} @:{'terms.glossary.wettbewerbsf'}?@:{'terms.glossary.higen'} @:{'terms.glossary.preisen'}",
|
"benefit_wash_desc": "@.capitalize:{'terms.glossary.nutzen'} @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.unser'} @:{'terms.glossary.nationales'} @:{'terms.glossary.netz'} @:{'terms.glossary.von'} @:{'terms.glossary.waschhallen'} @:{'terms.glossary.zu'} @:{'terms.glossary.wettbewerbsf'}?@:{'terms.glossary.higen'} @:{'terms.glossary.preisen'}",
|
||||||
|
"ean_label": "@:{'phrases.compat.admin.pos.ean'}",
|
||||||
"email_label": "Firmen-@.upper:{'terms.glossary.e'}-@:{'terms.glossary.mail'}",
|
"email_label": "Firmen-@.upper:{'terms.glossary.e'}-@:{'terms.glossary.mail'}",
|
||||||
"intro": "@.capitalize:{'terms.glossary.als'} @.capitalize:{'terms.glossary.kunde'} @:{'terms.glossary.bei'} @:{'terms.glossary.truck'} @.capitalize:{'terms.glossary.wash'} @:{'terms.glossary.erhalten'} @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.zugang'} @:{'terms.glossary.zu'}:",
|
"intro": "@.capitalize:{'terms.glossary.als'} @.capitalize:{'terms.glossary.kunde'} @:{'terms.glossary.bei'} @:{'terms.glossary.truck'} @.capitalize:{'terms.glossary.wash'} @:{'terms.glossary.erhalten'} @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.zugang'} @:{'terms.glossary.zu'}:",
|
||||||
"phone_label": "@:{'phrases.compat.admin.pos.company_phone'}",
|
"phone_label": "@:{'phrases.compat.admin.pos.company_phone'}",
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Fehler melden",
|
"button": "Fehler melden",
|
||||||
"title": "Fehler melden",
|
"title": "Fehler melden",
|
||||||
"subtitle": "Sende den aktuellen Bildschirm und die letzten Fehlerdetails an den Support.",
|
"subtitle": "Sende die letzten Fehlerdetails an den Support. Ein Screenshot wird angehängt, wenn möglich.",
|
||||||
"before_error": "Was haben Sie getan, bevor der Fehler auftrat?",
|
"before_error": "Was haben Sie getan, bevor der Fehler auftrat?",
|
||||||
"expected": "Was hatten Sie erwartet?",
|
"expected": "Was hatten Sie erwartet?",
|
||||||
"actual": "Was ist tatsächlich passiert?",
|
"actual": "Was ist tatsächlich passiert?",
|
||||||
"before_error_placeholder": "Beschreiben Sie die Aktion, zum Beispiel Bestellungen öffnen oder einen Kunden auswählen.",
|
"before_error_placeholder": "Beschreiben Sie die Aktion, zum Beispiel Bestellungen öffnen oder einen Kunden auswählen.",
|
||||||
"expected_placeholder": "Beschreiben Sie das erwartete Ergebnis.",
|
"expected_placeholder": "Beschreiben Sie das erwartete Ergebnis.",
|
||||||
"actual_placeholder": "Beschreiben Sie, was stattdessen zu sehen war, inklusive Fehlermeldung.",
|
"actual_placeholder": "Beschreiben Sie, was stattdessen zu sehen war, inklusive Fehlermeldung.",
|
||||||
"consent": "Ich akzeptiere, dass der aktuelle App-Bildschirm, letzte Request-Fehler, Vue-Fehler, Browserdetails und meine Antworten zur Fehlersuche erfasst werden.",
|
"consent": "Ich akzeptiere, dass letzte Request-Fehler, Vue-Fehler, Browserdetails, meine Antworten und ein App-Screenshot, wenn möglich, zur Fehlersuche erfasst werden.",
|
||||||
"submit": "Bericht senden",
|
"submit": "Bericht senden",
|
||||||
"submitted": "Fehlerbericht gesendet.",
|
"submitted": "Fehlerbericht gesendet.",
|
||||||
"capture_failed": "Der Screenshot konnte nicht erstellt werden. Bitte versuchen Sie es erneut.",
|
"capture_failed": "Der Screenshot konnte nicht erstellt werden. Der Bericht wird ohne Screenshot gesendet.",
|
||||||
"submit_failed": "Der Fehlerbericht konnte nicht gesendet werden.",
|
"submit_failed": "Der Fehlerbericht konnte nicht gesendet werden.",
|
||||||
"required": "Alle Felder und die Zustimmung zur Datenerfassung sind erforderlich.",
|
"required": "Alle Felder und die Zustimmung zur Datenerfassung sind erforderlich.",
|
||||||
"page_subtitle": "Gemeldete Bildschirme, Request-Fehler und Vue-Fehler",
|
"page_subtitle": "Gemeldete Bildschirme, Request-Fehler und Vue-Fehler",
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compat": {
|
||||||
|
"invoicing_period": {
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Zusammen abrechnen",
|
||||||
|
"split_by_month": "Nach Monat aufteilen",
|
||||||
|
"split_error_title": "Monatsaufteilung fehlgeschlagen",
|
||||||
|
"split_success_text": "{processed} Rechnungssammlungen verarbeitet. {changed} aufgeteilt, {skipped} übersprungen.",
|
||||||
|
"split_success_title": "Monatsaufteilung abgeschlossen",
|
||||||
|
"text": "Sie sind dabei, Aufträge aus mehreren Monaten gemeinsam abzurechnen ({months}). Sollen sie stattdessen nach Monat aufgeteilt werden?",
|
||||||
|
"title": "Aufträge aus mehreren Monaten"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"compat": {
|
||||||
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Zugriffsbereich",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Gruppe leeren",
|
||||||
|
"reload": "Neu laden",
|
||||||
|
"retry": "Erneut versuchen",
|
||||||
|
"select_all": "Gruppe auswaehlen"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Aktiviert",
|
||||||
|
"permission": "Berechtigung",
|
||||||
|
"route": "Route",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Erlaubt Zugriff auf {name}.",
|
||||||
|
"empty": "Es sind keine Berechtigungen verfuegbar.",
|
||||||
|
"empty_group": "Diese Gruppe hat keine Berechtigungen.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Rollenberechtigungen konnten nicht geladen werden.",
|
||||||
|
"update": "Eine oder mehrere Berechtigungen konnten nicht aktualisiert werden. Die aktuellen Rollenberechtigungen wurden neu geladen."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} Treffer",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Seitentyp- und Abteilungszugriff.",
|
||||||
|
"label": "Zugriff"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Berechtigungen fuer Buchungen, Zeitbuchungen und Planung.",
|
||||||
|
"label": "Buchungen"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Berechtigungen fuer Kunden, Benutzer, Mitarbeiter, Rollen und Anmeldung.",
|
||||||
|
"label": "Kunden und Benutzer"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Abteilungsbetrieb, Spuren, Relais, Benachrichtigungen, Berichte und Ziele.",
|
||||||
|
"label": "Abteilungen und Betrieb"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Berechtigungen fuer Rechnungen, Abrechnungsperioden, Inkasso und e-conomic.",
|
||||||
|
"label": "Rechnungen und e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Berechtigungen fuer Auftraege, POS, Zahlungen und Auftragszeilen.",
|
||||||
|
"label": "Auftraege und POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Berechtigungen, die keiner anderen Gruppe entsprechen.",
|
||||||
|
"label": "Sonstige"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Berechtigungen fuer Produkte, Preise, Rabatte und Waehrungen.",
|
||||||
|
"label": "Produkte und Preise"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Berechtigungen fuer Self-Service, Waschen, Scanner, Gateways und Edge-Hardware.",
|
||||||
|
"label": "Self-Service und Edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, Konfiguration, Release, Replikation und Integrationen.",
|
||||||
|
"label": "System und Konfiguration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "Keine Beschreibung verfuegbar.",
|
||||||
|
"no_search_results": "Keine Berechtigungen entsprechen der Suche.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Erlaubt Zugriff auf Admin-Dashboardseiten.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Erlaubt Zugriff auf Superuser-Dashboardseiten.",
|
||||||
|
"label": "Superuser"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Erlaubt Zugriff auf Benutzer-Dashboardseiten.",
|
||||||
|
"label": "Benutzer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Berechtigungen suchen",
|
||||||
|
"search_placeholder": "Nach Label, Schluessel, Beschreibung, Route oder Methode suchen",
|
||||||
|
"selected_summary": "{selected} von {total} ausgewaehlt",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Deaktiviert",
|
||||||
|
"enabled": "Aktiviert"
|
||||||
|
},
|
||||||
|
"subtitle": "Rollenberechtigungen",
|
||||||
|
"title": "Berechtigungen fuer Rolle {id}",
|
||||||
|
"title_with_name": "Berechtigungen fuer {name}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,31 @@
|
|||||||
"transaction_history": "@:{'phrases.compat.superuser.nav.orders'}"
|
"transaction_history": "@:{'phrases.compat.superuser.nav.orders'}"
|
||||||
},
|
},
|
||||||
"other": "@.capitalize:{'terms.glossary.andere'}",
|
"other": "@.capitalize:{'terms.glossary.andere'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Aktive Fahrzeuge",
|
||||||
|
"attributes": "Attribute",
|
||||||
|
"connected": "Verbunden",
|
||||||
|
"customer_management": "Kundenverwaltung",
|
||||||
|
"customer_number_required": "Eine Kundennummer ist erforderlich, bevor Kundeneinstellungen verwaltet werden können.",
|
||||||
|
"customer_rules": "Kundenregeln",
|
||||||
|
"effective_access": "Effektiver Zugriff",
|
||||||
|
"loaded": "Geladen",
|
||||||
|
"load_failed": "Benutzerdaten konnten nicht geladen werden",
|
||||||
|
"loading_user": "Benutzer wird geladen",
|
||||||
|
"management_hub": "Verwaltung",
|
||||||
|
"missing": "Fehlt",
|
||||||
|
"more_permissions": "+{count} weitere Berechtigungen",
|
||||||
|
"no_subscription_transactions": "Für diesen Kunden sind keine Waschabonnement-Transaktionen registriert.",
|
||||||
|
"not_invoiced": "Nicht fakturiert",
|
||||||
|
"open_draft": "Offener Entwurf",
|
||||||
|
"open_vehicles": "Fahrzeuge öffnen",
|
||||||
|
"price_overrides": "Preisüberschreibungen",
|
||||||
|
"pricing": "Preise",
|
||||||
|
"subscription_invoicing": "Abonnementabrechnung",
|
||||||
|
"subtitle": "Überblick über Kundendaten, Zugriff, Preise, Fahrzeuge und Abonnementabrechnung.",
|
||||||
|
"vehicles_load_failed": "Fahrzeuge konnten nicht geladen werden.",
|
||||||
|
"workspace_shortcuts": "Arbeitsbereich"
|
||||||
|
},
|
||||||
"permissions": "Tilladelser",
|
"permissions": "Tilladelser",
|
||||||
"user_data": "Brugerdata",
|
"user_data": "Brugerdata",
|
||||||
"user_id": "@:{'terms.glossary.benutzer'}-@.upper:{'terms.glossary.id'}",
|
"user_id": "@:{'terms.glossary.benutzer'}-@.upper:{'terms.glossary.id'}",
|
||||||
|
|||||||
@@ -28,13 +28,16 @@
|
|||||||
"title": "@:{'phrases.compat.limited_backoffice.forbidden.title'}",
|
"title": "@:{'phrases.compat.limited_backoffice.forbidden.title'}",
|
||||||
"department": "@:{'phrases.compat.limited_backoffice.forbidden.department'}"
|
"department": "@:{'phrases.compat.limited_backoffice.forbidden.department'}"
|
||||||
},
|
},
|
||||||
"prices": {
|
"prices": {
|
||||||
"title": "@:{'phrases.compat.limited_backoffice.prices.title'}",
|
"title": "@:{'phrases.compat.limited_backoffice.prices.title'}",
|
||||||
"save": "@:{'phrases.compat.limited_backoffice.prices.save'}",
|
"save": "@:{'phrases.compat.limited_backoffice.prices.save'}",
|
||||||
"saved": "@:{'phrases.compat.limited_backoffice.prices.saved'}",
|
"saved": "@:{'phrases.compat.limited_backoffice.prices.saved'}",
|
||||||
"product": "@:{'phrases.compat.limited_backoffice.prices.product'}",
|
"autosave_saving": "@:{'phrases.compat.limited_backoffice.prices.autosave_saving'}",
|
||||||
"price": "@:{'phrases.compat.limited_backoffice.prices.price'}",
|
"autosave_pending": "@:{'phrases.compat.limited_backoffice.prices.autosave_pending'}",
|
||||||
"price_required": "@:{'phrases.compat.limited_backoffice.prices.price_required'}",
|
"autosave_saved": "@:{'phrases.compat.limited_backoffice.prices.autosave_saved'}",
|
||||||
|
"product": "@:{'phrases.compat.limited_backoffice.prices.product'}",
|
||||||
|
"price": "@:{'phrases.compat.limited_backoffice.prices.price'}",
|
||||||
|
"price_required": "@:{'phrases.compat.limited_backoffice.prices.price_required'}",
|
||||||
"setup_required_title": "@:{'phrases.compat.limited_backoffice.prices.setup_required_title'}",
|
"setup_required_title": "@:{'phrases.compat.limited_backoffice.prices.setup_required_title'}",
|
||||||
"setup_required_message": "@:{'phrases.compat.limited_backoffice.prices.setup_required_message'}",
|
"setup_required_message": "@:{'phrases.compat.limited_backoffice.prices.setup_required_message'}",
|
||||||
"no_products": "@:{'phrases.compat.limited_backoffice.prices.no_products'}"
|
"no_products": "@:{'phrases.compat.limited_backoffice.prices.no_products'}"
|
||||||
@@ -46,6 +49,15 @@
|
|||||||
"edit_title": "@:{'phrases.compat.limited_backoffice.employees.edit_title'}",
|
"edit_title": "@:{'phrases.compat.limited_backoffice.employees.edit_title'}",
|
||||||
"name": "@:{'phrases.compat.limited_backoffice.employees.name'}",
|
"name": "@:{'phrases.compat.limited_backoffice.employees.name'}",
|
||||||
"email": "@:{'phrases.compat.limited_backoffice.employees.email'}",
|
"email": "@:{'phrases.compat.limited_backoffice.employees.email'}",
|
||||||
|
"user_id": "@:{'phrases.compat.limited_backoffice.employees.user_id'}",
|
||||||
|
"phone": "@:{'phrases.compat.limited_backoffice.employees.phone'}",
|
||||||
|
"phone_validation": "@:{'phrases.compat.limited_backoffice.employees.phone_validation'}",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "@:{'phrases.compat.limited_backoffice.employees.country_codes.denmark'}",
|
||||||
|
"sweden": "@:{'phrases.compat.limited_backoffice.employees.country_codes.sweden'}",
|
||||||
|
"norway": "@:{'phrases.compat.limited_backoffice.employees.country_codes.norway'}",
|
||||||
|
"finland": "@:{'phrases.compat.limited_backoffice.employees.country_codes.finland'}"
|
||||||
|
},
|
||||||
"password": "@:{'phrases.compat.limited_backoffice.employees.password'}",
|
"password": "@:{'phrases.compat.limited_backoffice.employees.password'}",
|
||||||
"password_create_help": "@:{'phrases.compat.limited_backoffice.employees.password_create_help'}",
|
"password_create_help": "@:{'phrases.compat.limited_backoffice.employees.password_create_help'}",
|
||||||
"password_update_help": "@:{'phrases.compat.limited_backoffice.employees.password_update_help'}",
|
"password_update_help": "@:{'phrases.compat.limited_backoffice.employees.password_update_help'}",
|
||||||
@@ -64,7 +76,13 @@
|
|||||||
"active": "@:{'phrases.compat.limited_backoffice.employees.active'}",
|
"active": "@:{'phrases.compat.limited_backoffice.employees.active'}",
|
||||||
"inactive": "@:{'phrases.compat.limited_backoffice.employees.inactive'}",
|
"inactive": "@:{'phrases.compat.limited_backoffice.employees.inactive'}",
|
||||||
"edit": "@:{'phrases.compat.limited_backoffice.employees.edit'}",
|
"edit": "@:{'phrases.compat.limited_backoffice.employees.edit'}",
|
||||||
"deactivate": "@:{'phrases.compat.limited_backoffice.employees.deactivate'}"
|
"deactivate": "@:{'phrases.compat.limited_backoffice.employees.deactivate'}",
|
||||||
|
"login_link": "@:{'phrases.compat.limited_backoffice.employees.login_link'}",
|
||||||
|
"login_link_title": "@:{'phrases.compat.limited_backoffice.employees.login_link_title'}",
|
||||||
|
"login_link_qr_alt": "@:{'phrases.compat.limited_backoffice.employees.login_link_qr_alt'}",
|
||||||
|
"login_link_url": "@:{'phrases.compat.limited_backoffice.employees.login_link_url'}",
|
||||||
|
"copy_login_link": "@:{'phrases.compat.limited_backoffice.employees.copy_login_link'}",
|
||||||
|
"login_link_copied": "@:{'phrases.compat.limited_backoffice.employees.login_link_copied'}"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -94,7 +112,132 @@
|
|||||||
"save_prices": "@:{'phrases.compat.limited_backoffice.errors.save_prices'}",
|
"save_prices": "@:{'phrases.compat.limited_backoffice.errors.save_prices'}",
|
||||||
"load_employees": "@:{'phrases.compat.limited_backoffice.errors.load_employees'}",
|
"load_employees": "@:{'phrases.compat.limited_backoffice.errors.load_employees'}",
|
||||||
"save_employee": "@:{'phrases.compat.limited_backoffice.errors.save_employee'}",
|
"save_employee": "@:{'phrases.compat.limited_backoffice.errors.save_employee'}",
|
||||||
"deactivate_employee": "@:{'phrases.compat.limited_backoffice.errors.deactivate_employee'}"
|
"deactivate_employee": "@:{'phrases.compat.limited_backoffice.errors.deactivate_employee'}",
|
||||||
|
"generate_employee_login_link": "@:{'phrases.compat.limited_backoffice.errors.generate_employee_login_link'}"
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "@:{'phrases.compat.limited_backoffice.role_permissions.title'}",
|
||||||
|
"tooltip": "@:{'phrases.compat.limited_backoffice.role_permissions.tooltip'}",
|
||||||
|
"intro": "@:{'phrases.compat.limited_backoffice.role_permissions.intro'}",
|
||||||
|
"scope": "@:{'phrases.compat.limited_backoffice.role_permissions.scope'}",
|
||||||
|
"selected": "@:{'phrases.compat.limited_backoffice.role_permissions.selected'}",
|
||||||
|
"close": "@:{'phrases.compat.limited_backoffice.role_permissions.close'}",
|
||||||
|
"unavailable": "@:{'phrases.compat.limited_backoffice.role_permissions.unavailable'}",
|
||||||
|
"groups": {
|
||||||
|
"account": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.account'}",
|
||||||
|
"orders": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.orders'}",
|
||||||
|
"bookings": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.bookings'}",
|
||||||
|
"time_bookings": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.time_bookings'}",
|
||||||
|
"reports": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.reports'}",
|
||||||
|
"limited_backoffice": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.limited_backoffice'}",
|
||||||
|
"unknown": "@:{'phrases.compat.limited_backoffice.role_permissions.groups.unknown'}"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.sign_in.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.sign_in.description'}"
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_permissions.description'}"
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_orders.description'}"
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_orders.description'}"
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_orders.description'}"
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.delete_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.delete_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_items.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_items.description'}"
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_order_items.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_order_items.description'}"
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.remove_order_lines.description'}"
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.charge_orders.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.charge_orders.description'}"
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_department_bookings.description'}"
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_own_bookings.description'}"
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.update_bookings.description'}"
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_bookings.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_bookings.description'}"
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.mark_bookings_complete.description'}"
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.send_booking_confirmations.description'}"
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.create_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.edit_time_booking_entries.description'}"
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_order_statistics.description'}"
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.view_booking_statistics.description'}"
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.open_limited_backoffice.description'}"
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_department_prices.description'}"
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.manage_employee_access.description'}"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.unknown.label'}",
|
||||||
|
"description": "@:{'phrases.compat.limited_backoffice.role_permissions.capabilities.unknown.description'}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -141,6 +284,9 @@
|
|||||||
"title": "@.capitalize:{'terms.glossary.abteilungspreise'}",
|
"title": "@.capitalize:{'terms.glossary.abteilungspreise'}",
|
||||||
"save": "@:{'terms.glossary.preise'} @:{'terms.glossary.speichern'}",
|
"save": "@:{'terms.glossary.preise'} @:{'terms.glossary.speichern'}",
|
||||||
"saved": "@:{'terms.glossary.preise'} @:{'terms.glossary.gespeichert'}.",
|
"saved": "@:{'terms.glossary.preise'} @:{'terms.glossary.gespeichert'}.",
|
||||||
|
"autosave_saving": "Preise werden gespeichert...",
|
||||||
|
"autosave_pending": "Preisaenderungen ausstehend.",
|
||||||
|
"autosave_saved": "Alle Preisaenderungen gespeichert.",
|
||||||
"product": "@.capitalize:{'terms.glossary.produkt'}",
|
"product": "@.capitalize:{'terms.glossary.produkt'}",
|
||||||
"price": "Abteilungspreis",
|
"price": "Abteilungspreis",
|
||||||
"price_required": "Jedes @.capitalize:{'terms.glossary.produkt'} @:{'terms.glossary.muss'} einen @:{'terms.glossary.nicht'}-negativen numerischen Preis @:{'terms.glossary.haben'}.",
|
"price_required": "Jedes @.capitalize:{'terms.glossary.produkt'} @:{'terms.glossary.muss'} einen @:{'terms.glossary.nicht'}-negativen numerischen Preis @:{'terms.glossary.haben'}.",
|
||||||
@@ -155,6 +301,15 @@
|
|||||||
"edit_title": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.bearbeiten'}",
|
"edit_title": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.bearbeiten'}",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
|
"user_id": "Benutzer-ID",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"phone_validation": "Die Telefonnummer muss 4-15 Ziffern enthalten.",
|
||||||
|
"country_codes": {
|
||||||
|
"denmark": "Dänemark",
|
||||||
|
"sweden": "Schweden",
|
||||||
|
"norway": "Norwegen",
|
||||||
|
"finland": "Finnland"
|
||||||
|
},
|
||||||
"password": "@:{'terms.glossary.passwort'}",
|
"password": "@:{'terms.glossary.passwort'}",
|
||||||
"password_create_help": "@.capitalize:{'terms.glossary.fur'} neue @:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.erforderlich'}. Verwenden @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.mindestens'} 8 Zeichen.",
|
"password_create_help": "@.capitalize:{'terms.glossary.fur'} neue @:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.erforderlich'}. Verwenden @.capitalize:{'terms.glossary.sie'} @:{'terms.glossary.mindestens'} 8 Zeichen.",
|
||||||
"password_update_help": "Leer lassen, um das vorhandene @:{'terms.glossary.passwort'} beizubehalten.",
|
"password_update_help": "Leer lassen, um das vorhandene @:{'terms.glossary.passwort'} beizubehalten.",
|
||||||
@@ -173,7 +328,13 @@
|
|||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"edit": "@.capitalize:{'terms.glossary.bearbeiten'}",
|
"edit": "@.capitalize:{'terms.glossary.bearbeiten'}",
|
||||||
"deactivate": "Deaktivieren"
|
"deactivate": "Deaktivieren",
|
||||||
|
"login_link": "QR-Login",
|
||||||
|
"login_link_title": "Mitarbeiter-Loginlink",
|
||||||
|
"login_link_qr_alt": "QR-Code-Loginlink",
|
||||||
|
"login_link_url": "Loginlink",
|
||||||
|
"copy_login_link": "Link kopieren",
|
||||||
|
"login_link_copied": "Link kopiert."
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"viewer": {
|
"viewer": {
|
||||||
@@ -203,7 +364,132 @@
|
|||||||
"save_prices": "@.capitalize:{'terms.glossary.abteilungspreise'} @:{'terms.glossary.konnten'} @:{'terms.glossary.nicht'} @:{'terms.glossary.gespeichert'} @:{'terms.glossary.werden'}.",
|
"save_prices": "@.capitalize:{'terms.glossary.abteilungspreise'} @:{'terms.glossary.konnten'} @:{'terms.glossary.nicht'} @:{'terms.glossary.gespeichert'} @:{'terms.glossary.werden'}.",
|
||||||
"load_employees": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.konnten'} @:{'terms.glossary.nicht'} @:{'terms.glossary.geladen'} @:{'terms.glossary.werden'}.",
|
"load_employees": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.konnten'} @:{'terms.glossary.nicht'} @:{'terms.glossary.geladen'} @:{'terms.glossary.werden'}.",
|
||||||
"save_employee": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.konnte'} @:{'terms.glossary.nicht'} @:{'terms.glossary.gespeichert'} @:{'terms.glossary.werden'}.",
|
"save_employee": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.konnte'} @:{'terms.glossary.nicht'} @:{'terms.glossary.gespeichert'} @:{'terms.glossary.werden'}.",
|
||||||
"deactivate_employee": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.konnte'} @:{'terms.glossary.nicht'} @:{'terms.glossary.deaktiviert'} @:{'terms.glossary.werden'}."
|
"deactivate_employee": "@:{'terms.glossary.mitarbeiter'} @:{'terms.glossary.konnte'} @:{'terms.glossary.nicht'} @:{'terms.glossary.deaktiviert'} @:{'terms.glossary.werden'}.",
|
||||||
|
"generate_employee_login_link": "Mitarbeiter-Loginlink konnte nicht erstellt werden."
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"title": "Rollenberechtigungen",
|
||||||
|
"tooltip": "Rollenberechtigungen anzeigen",
|
||||||
|
"intro": "Rollen bündeln die genauen Funktionen, die einem Mitarbeiter beim Speichern der Rolle zugewiesen werden.",
|
||||||
|
"scope": "Jede Funktion ist auf die Abteilungen begrenzt, die für den Mitarbeiter ausgewählt sind.",
|
||||||
|
"selected": "Ausgewählt",
|
||||||
|
"close": "Schließen",
|
||||||
|
"unavailable": "Berechtigungsdetails sind für diese Rolle erst verfügbar, wenn der Rollenkatalog aktualisiert wurde.",
|
||||||
|
"groups": {
|
||||||
|
"account": "Konto",
|
||||||
|
"orders": "Aufträge",
|
||||||
|
"bookings": "Buchungen",
|
||||||
|
"time_bookings": "Zeitbuchungen",
|
||||||
|
"reports": "Berichte",
|
||||||
|
"limited_backoffice": "Backoffice",
|
||||||
|
"unknown": "Sonstiges"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"sign_in": {
|
||||||
|
"label": "Anmelden",
|
||||||
|
"description": "Erlaubt dem Mitarbeiter, sich bei Pleno anzumelden."
|
||||||
|
},
|
||||||
|
"view_own_permissions": {
|
||||||
|
"label": "Eigene Berechtigungen anzeigen",
|
||||||
|
"description": "Zeigt dem Mitarbeiter, welche Zugriffsrechte das Konto hat."
|
||||||
|
},
|
||||||
|
"view_orders": {
|
||||||
|
"label": "Aufträge anzeigen",
|
||||||
|
"description": "Kann Auftragslisten und Auftragsdetails für zugewiesene Abteilungen öffnen."
|
||||||
|
},
|
||||||
|
"create_orders": {
|
||||||
|
"label": "Aufträge erstellen",
|
||||||
|
"description": "Kann neue Aufträge für zugewiesene Abteilungen erstellen."
|
||||||
|
},
|
||||||
|
"edit_orders": {
|
||||||
|
"label": "Aufträge bearbeiten",
|
||||||
|
"description": "Kann Auftragsdetails für zugewiesene Abteilungen ändern."
|
||||||
|
},
|
||||||
|
"delete_orders": {
|
||||||
|
"label": "Aufträge löschen",
|
||||||
|
"description": "Kann Aufträge in zugewiesenen Abteilungen löschen."
|
||||||
|
},
|
||||||
|
"view_order_items": {
|
||||||
|
"label": "Auftragspositionen anzeigen",
|
||||||
|
"description": "Kann Produkte, Leistungen und Preise auf Aufträgen sehen."
|
||||||
|
},
|
||||||
|
"create_order_items": {
|
||||||
|
"label": "Auftragspositionen erstellen",
|
||||||
|
"description": "Kann Produkte und Leistungen zu Aufträgen hinzufügen."
|
||||||
|
},
|
||||||
|
"update_order_lines": {
|
||||||
|
"label": "Auftragspositionen bearbeiten",
|
||||||
|
"description": "Kann Produkte, Mengen und Preise auf Aufträgen ändern."
|
||||||
|
},
|
||||||
|
"remove_order_lines": {
|
||||||
|
"label": "Auftragspositionen löschen",
|
||||||
|
"description": "Kann Produkte und Leistungen aus Aufträgen entfernen."
|
||||||
|
},
|
||||||
|
"charge_orders": {
|
||||||
|
"label": "Aufträge abrechnen",
|
||||||
|
"description": "Kann Zahlungen annehmen oder eine Zahlungsreservierung für einen Auftrag belasten."
|
||||||
|
},
|
||||||
|
"view_department_bookings": {
|
||||||
|
"label": "Abteilungsbuchungen anzeigen",
|
||||||
|
"description": "Kann Buchungen für zugewiesene Abteilungen sehen."
|
||||||
|
},
|
||||||
|
"view_own_bookings": {
|
||||||
|
"label": "Eigene Buchungen anzeigen",
|
||||||
|
"description": "Kann Buchungszahlen und Buchungen sehen, die mit dem Mitarbeiterkonto verbunden sind."
|
||||||
|
},
|
||||||
|
"update_bookings": {
|
||||||
|
"label": "Buchungen bearbeiten",
|
||||||
|
"description": "Kann erlaubte Buchungsdetails ändern."
|
||||||
|
},
|
||||||
|
"create_bookings": {
|
||||||
|
"label": "Buchungen erstellen",
|
||||||
|
"description": "Kann neue Buchungen für zugewiesene Abteilungen erstellen."
|
||||||
|
},
|
||||||
|
"mark_bookings_complete": {
|
||||||
|
"label": "Buchungen abschließen",
|
||||||
|
"description": "Kann Buchungen als abgeschlossen markieren."
|
||||||
|
},
|
||||||
|
"send_booking_confirmations": {
|
||||||
|
"label": "Buchungsbestätigungen erneut senden",
|
||||||
|
"description": "Kann Buchungsbestätigungen und Abschluss-E-Mails erneut senden."
|
||||||
|
},
|
||||||
|
"view_time_booking_entries": {
|
||||||
|
"label": "Zeitbuchungen anzeigen",
|
||||||
|
"description": "Kann Zeitbuchungseinträge für zugewiesene Abteilungen prüfen."
|
||||||
|
},
|
||||||
|
"create_time_booking_entries": {
|
||||||
|
"label": "Zeitbuchungen erstellen",
|
||||||
|
"description": "Kann neue Zeitbuchungseinträge hinzufügen."
|
||||||
|
},
|
||||||
|
"edit_time_booking_entries": {
|
||||||
|
"label": "Zeitbuchungen bearbeiten",
|
||||||
|
"description": "Kann Zeitbuchungseinträge bearbeiten, wenn die Backend-Route dies unterstützt."
|
||||||
|
},
|
||||||
|
"view_order_statistics": {
|
||||||
|
"label": "Auftragsstatistik anzeigen",
|
||||||
|
"description": "Kann Auftragsstatistiken für zugewiesene Abteilungen öffnen."
|
||||||
|
},
|
||||||
|
"view_booking_statistics": {
|
||||||
|
"label": "Buchungsstatistik anzeigen",
|
||||||
|
"description": "Kann Buchungsstatistiken für zugewiesene Abteilungen öffnen."
|
||||||
|
},
|
||||||
|
"open_limited_backoffice": {
|
||||||
|
"label": "Begrenztes Backoffice öffnen",
|
||||||
|
"description": "Kann den begrenzten Backofficebereich öffnen."
|
||||||
|
},
|
||||||
|
"manage_department_prices": {
|
||||||
|
"label": "Abteilungspreise verwalten",
|
||||||
|
"description": "Kann explizite Preise für zugewiesene Abteilungen bearbeiten."
|
||||||
|
},
|
||||||
|
"manage_employee_access": {
|
||||||
|
"label": "Mitarbeiterzugang verwalten",
|
||||||
|
"description": "Kann begrenzte Backoffice-Mitarbeiter erstellen, bearbeiten und deaktivieren."
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"label": "Zusätzliche Funktion",
|
||||||
|
"description": "Diese Funktion kommt aus dem Rollenkatalog, ist aber in der Oberfläche noch nicht beschrieben."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,7 @@
|
|||||||
"company_phone": "@.capitalize:{'terms.glossary.company'} @:{'terms.glossary.phone'}",
|
"company_phone": "@.capitalize:{'terms.glossary.company'} @:{'terms.glossary.phone'}",
|
||||||
"contact_email": "@.capitalize:{'terms.glossary.contact'} @:{'terms.glossary.email'}",
|
"contact_email": "@.capitalize:{'terms.glossary.contact'} @:{'terms.glossary.email'}",
|
||||||
"contact_phone": "@.capitalize:{'terms.glossary.contact'} @:{'terms.glossary.phone'}",
|
"contact_phone": "@.capitalize:{'terms.glossary.contact'} @:{'terms.glossary.phone'}",
|
||||||
|
"ean": "EAN",
|
||||||
"created_by": "@.capitalize:{'terms.glossary.created'} @:{'terms.glossary.by'}",
|
"created_by": "@.capitalize:{'terms.glossary.created'} @:{'terms.glossary.by'}",
|
||||||
"customer_conflict": {
|
"customer_conflict": {
|
||||||
"help_text": "@.capitalize:{'terms.glossary.this'} @:{'terms.glossary.typed'} @:{'terms.glossary.plate'} @:{'terms.glossary.belongs'} @:{'terms.glossary.to'} @:{'terms.glossary.another'} @:{'terms.glossary.saved'} @:{'terms.glossary.customer'}. @.capitalize:{'terms.glossary.choose'} @:{'terms.glossary.whether'} @:{'terms.glossary.to'} @:{'terms.glossary.keep'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.selected'} @:{'terms.glossary.customer'} @:{'terms.glossary.or'} @:{'terms.glossary.switch'} @:{'terms.glossary.to'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.vehicle'} @:{'terms.glossary.customer'}.",
|
"help_text": "@.capitalize:{'terms.glossary.this'} @:{'terms.glossary.typed'} @:{'terms.glossary.plate'} @:{'terms.glossary.belongs'} @:{'terms.glossary.to'} @:{'terms.glossary.another'} @:{'terms.glossary.saved'} @:{'terms.glossary.customer'}. @.capitalize:{'terms.glossary.choose'} @:{'terms.glossary.whether'} @:{'terms.glossary.to'} @:{'terms.glossary.keep'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.selected'} @:{'terms.glossary.customer'} @:{'terms.glossary.or'} @:{'terms.glossary.switch'} @:{'terms.glossary.to'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.vehicle'} @:{'terms.glossary.customer'}.",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"benefit_portal": "@.capitalize:{'terms.glossary.customer'} @.capitalize:{'terms.glossary.portal'}",
|
"benefit_portal": "@.capitalize:{'terms.glossary.customer'} @.capitalize:{'terms.glossary.portal'}",
|
||||||
"benefit_portal_desc": "@.capitalize:{'terms.glossary.use'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.customer'} @:{'terms.glossary.portal'} @:{'terms.glossary.to'} @:{'terms.glossary.add'} @:{'terms.glossary.vehicles'}, @:{'terms.glossary.book'} @:{'terms.glossary.washes'}, @:{'terms.glossary.download'} @:{'terms.glossary.invoices'}, @:{'terms.glossary.wash'} @:{'terms.glossary.certificates'} @:{'terms.glossary.and'} @:{'terms.glossary.information'} @:{'terms.glossary.for'} @:{'terms.glossary.historical'} @:{'terms.glossary.washes'}",
|
"benefit_portal_desc": "@.capitalize:{'terms.glossary.use'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.customer'} @:{'terms.glossary.portal'} @:{'terms.glossary.to'} @:{'terms.glossary.add'} @:{'terms.glossary.vehicles'}, @:{'terms.glossary.book'} @:{'terms.glossary.washes'}, @:{'terms.glossary.download'} @:{'terms.glossary.invoices'}, @:{'terms.glossary.wash'} @:{'terms.glossary.certificates'} @:{'terms.glossary.and'} @:{'terms.glossary.information'} @:{'terms.glossary.for'} @:{'terms.glossary.historical'} @:{'terms.glossary.washes'}",
|
||||||
"benefit_wash_desc": "@.capitalize:{'terms.glossary.use'} @:{'terms.glossary.our'} @:{'terms.glossary.national'} @:{'terms.glossary.network'} @:{'terms.glossary.of'} @:{'terms.glossary.wash'} @:{'terms.glossary.halls'} @:{'terms.glossary.at'} @:{'terms.glossary.competitive'} @:{'terms.glossary.prices'}",
|
"benefit_wash_desc": "@.capitalize:{'terms.glossary.use'} @:{'terms.glossary.our'} @:{'terms.glossary.national'} @:{'terms.glossary.network'} @:{'terms.glossary.of'} @:{'terms.glossary.wash'} @:{'terms.glossary.halls'} @:{'terms.glossary.at'} @:{'terms.glossary.competitive'} @:{'terms.glossary.prices'}",
|
||||||
|
"ean_label": "@:{'phrases.compat.admin.pos.ean'}",
|
||||||
"email_label": "@.capitalize:{'terms.glossary.company'} @:{'terms.glossary.email'}",
|
"email_label": "@.capitalize:{'terms.glossary.company'} @:{'terms.glossary.email'}",
|
||||||
"intro": "@.capitalize:{'terms.glossary.as'} @:{'terms.glossary.a'} @:{'terms.glossary.customer'} @:{'terms.glossary.at'} @.capitalize:{'terms.glossary.truck'} @.capitalize:{'terms.glossary.wash'} @:{'terms.glossary.you'} @:{'terms.glossary.get'} @:{'terms.glossary.access'} @:{'terms.glossary.to'}:",
|
"intro": "@.capitalize:{'terms.glossary.as'} @:{'terms.glossary.a'} @:{'terms.glossary.customer'} @:{'terms.glossary.at'} @.capitalize:{'terms.glossary.truck'} @.capitalize:{'terms.glossary.wash'} @:{'terms.glossary.you'} @:{'terms.glossary.get'} @:{'terms.glossary.access'} @:{'terms.glossary.to'}:",
|
||||||
"phone_label": "@:{'phrases.compat.admin.pos.company_phone'}",
|
"phone_label": "@:{'phrases.compat.admin.pos.company_phone'}",
|
||||||
|
|||||||
@@ -21,6 +21,13 @@
|
|||||||
"opening_hours": {
|
"opening_hours": {
|
||||||
"closed": "@:{'phrases.compat.global.closed'}"
|
"closed": "@:{'phrases.compat.global.closed'}"
|
||||||
},
|
},
|
||||||
|
"pricing": {
|
||||||
|
"custom_pricing_disabled": "Fallback enabled",
|
||||||
|
"custom_pricing_enabled": "Custom only",
|
||||||
|
"custom_pricing_missing_price": "Missing department prices resolve to 999999.",
|
||||||
|
"custom_pricing_only": "No fallback pricing",
|
||||||
|
"effective_department_price": "Effective department price"
|
||||||
|
},
|
||||||
"search_placeholder": "@.capitalize:{'terms.glossary.search'} @:{'terms.glossary.by'} @:{'terms.glossary.department'} @:{'terms.glossary.name'}",
|
"search_placeholder": "@.capitalize:{'terms.glossary.search'} @:{'terms.glossary.by'} @:{'terms.glossary.department'} @:{'terms.glossary.name'}",
|
||||||
"select_department": "@.capitalize:{'terms.glossary.select'} @:{'terms.glossary.a'} @:{'terms.glossary.department'}",
|
"select_department": "@.capitalize:{'terms.glossary.select'} @:{'terms.glossary.a'} @:{'terms.glossary.department'}",
|
||||||
"subtitle": "@.capitalize:{'terms.glossary.manage'} @:{'terms.glossary.departments'}",
|
"subtitle": "@.capitalize:{'terms.glossary.manage'} @:{'terms.glossary.departments'}",
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
"error_report": {
|
"error_report": {
|
||||||
"button": "Report error",
|
"button": "Report error",
|
||||||
"title": "Report error",
|
"title": "Report error",
|
||||||
"subtitle": "Send the current screen and recent error details to support.",
|
"subtitle": "Send recent error details to support. A screenshot is attached when available.",
|
||||||
"before_error": "What were you doing before the error occurred?",
|
"before_error": "What were you doing before the error occurred?",
|
||||||
"expected": "What did you expect would happen?",
|
"expected": "What did you expect would happen?",
|
||||||
"actual": "What actually happened?",
|
"actual": "What actually happened?",
|
||||||
"before_error_placeholder": "Describe the action you were taking, for example opening orders or selecting a customer.",
|
"before_error_placeholder": "Describe the action you were taking, for example opening orders or selecting a customer.",
|
||||||
"expected_placeholder": "Describe the result you expected to see.",
|
"expected_placeholder": "Describe the result you expected to see.",
|
||||||
"actual_placeholder": "Describe what you saw instead, including any error text.",
|
"actual_placeholder": "Describe what you saw instead, including any error text.",
|
||||||
"consent": "I accept that the current app screen, recent request errors, Vue errors, browser details, and my answers are collected for troubleshooting.",
|
"consent": "I accept that recent request errors, Vue errors, browser details, my answers, and an app screenshot when available are collected for troubleshooting.",
|
||||||
"submit": "Submit report",
|
"submit": "Submit report",
|
||||||
"submitted": "Error report submitted.",
|
"submitted": "Error report submitted.",
|
||||||
"capture_failed": "The screen capture failed. Please try again.",
|
"capture_failed": "The screen capture failed. The report will be sent without a screenshot.",
|
||||||
"submit_failed": "The error report could not be submitted.",
|
"submit_failed": "The error report could not be submitted.",
|
||||||
"required": "All fields and data collection acceptance are required.",
|
"required": "All fields and data collection acceptance are required.",
|
||||||
"page_subtitle": "Reported screens, request failures, and Vue errors",
|
"page_subtitle": "Reported screens, request failures, and Vue errors",
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compat": {
|
||||||
|
"invoicing_period": {
|
||||||
|
"multi_month_invoice_warning": {
|
||||||
|
"invoice_together": "Invoice together",
|
||||||
|
"split_by_month": "Split by month",
|
||||||
|
"split_error_title": "Monthly split failed",
|
||||||
|
"split_success_text": "Processed {processed} invoice collections. Split {changed}, skipped {skipped}.",
|
||||||
|
"split_success_title": "Monthly split completed",
|
||||||
|
"text": "You are about to invoice orders from multiple months together ({months}). Should they be split by month instead?",
|
||||||
|
"title": "Orders from multiple months"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,6 +140,7 @@
|
|||||||
"departments": {
|
"departments": {
|
||||||
"columns": {
|
"columns": {
|
||||||
"archived": "@:{'terms.glossary.archived'}",
|
"archived": "@:{'terms.glossary.archived'}",
|
||||||
|
"custom_pricing_only": "No fallback pricing",
|
||||||
"dimension": "Dimension",
|
"dimension": "Dimension",
|
||||||
"economic_department": "@.upper:{'terms.glossary.e'}-@:{'terms.glossary.conomic'} @.capitalize:{'terms.glossary.department'}",
|
"economic_department": "@.upper:{'terms.glossary.e'}-@:{'terms.glossary.conomic'} @.capitalize:{'terms.glossary.department'}",
|
||||||
"latitude": "@:{'phrases.compat.departments.form.latitude'}",
|
"latitude": "@:{'phrases.compat.departments.form.latitude'}",
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"compat": {
|
||||||
|
"roles": {
|
||||||
|
"permissions": {
|
||||||
|
"access_scope": "Access scope",
|
||||||
|
"actions": {
|
||||||
|
"clear_all": "Clear group",
|
||||||
|
"reload": "Reload",
|
||||||
|
"retry": "Try again",
|
||||||
|
"select_all": "Select group"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"permission": "Permission",
|
||||||
|
"route": "Route",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"department_access_description": "Allows access to {name}.",
|
||||||
|
"empty": "No permissions are available.",
|
||||||
|
"empty_group": "This group has no permissions.",
|
||||||
|
"errors": {
|
||||||
|
"load": "Could not load role permissions.",
|
||||||
|
"update": "Could not update one or more permissions. The current role permissions were reloaded."
|
||||||
|
},
|
||||||
|
"filtered_summary": "{count} matching",
|
||||||
|
"groups": {
|
||||||
|
"access": {
|
||||||
|
"description": "Page type and department access permissions.",
|
||||||
|
"label": "Access"
|
||||||
|
},
|
||||||
|
"bookings": {
|
||||||
|
"description": "Booking, time booking, and planning permissions.",
|
||||||
|
"label": "Bookings"
|
||||||
|
},
|
||||||
|
"customers_users": {
|
||||||
|
"description": "Customer, user, employee, role, and authentication permissions.",
|
||||||
|
"label": "Customers and users"
|
||||||
|
},
|
||||||
|
"departments_operations": {
|
||||||
|
"description": "Department operations, lanes, relays, notifications, reports, and goals.",
|
||||||
|
"label": "Departments and operations"
|
||||||
|
},
|
||||||
|
"invoicing_economic": {
|
||||||
|
"description": "Invoice, billing period, collection, and e-conomic permissions.",
|
||||||
|
"label": "Invoicing and e-conomic"
|
||||||
|
},
|
||||||
|
"orders_pos": {
|
||||||
|
"description": "Order, POS, payment, and order line permissions.",
|
||||||
|
"label": "Orders and POS"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"description": "Permissions that do not match another group.",
|
||||||
|
"label": "Other"
|
||||||
|
},
|
||||||
|
"products_pricing": {
|
||||||
|
"description": "Product, price, discount, and currency permissions.",
|
||||||
|
"label": "Products and pricing"
|
||||||
|
},
|
||||||
|
"selfserve_edge": {
|
||||||
|
"description": "Self-serve, wash, scanner, gateway, and edge hardware permissions.",
|
||||||
|
"label": "Self-serve and edge"
|
||||||
|
},
|
||||||
|
"system_config": {
|
||||||
|
"description": "System, configuration, release, replication, and integration permissions.",
|
||||||
|
"label": "System and configuration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"no_description": "No description is available.",
|
||||||
|
"no_search_results": "No permissions match the current search.",
|
||||||
|
"page_access": {
|
||||||
|
"admin": {
|
||||||
|
"description": "Allows access to admin dashboard pages.",
|
||||||
|
"label": "Admin"
|
||||||
|
},
|
||||||
|
"superuser": {
|
||||||
|
"description": "Allows access to superuser dashboard pages.",
|
||||||
|
"label": "Superuser"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"description": "Allows access to user dashboard pages.",
|
||||||
|
"label": "User"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search_label": "Search permissions",
|
||||||
|
"search_placeholder": "Search by label, key, description, route, or method",
|
||||||
|
"selected_summary": "{selected} of {total} selected",
|
||||||
|
"status": {
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"enabled": "Enabled"
|
||||||
|
},
|
||||||
|
"subtitle": "Role permissions",
|
||||||
|
"title": "Role {id} permissions",
|
||||||
|
"title_with_name": "{name} permissions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,31 @@
|
|||||||
"transaction_history": "@.capitalize:{'terms.glossary.transaction'} @:{'terms.glossary.history'}"
|
"transaction_history": "@.capitalize:{'terms.glossary.transaction'} @:{'terms.glossary.history'}"
|
||||||
},
|
},
|
||||||
"other": "@:{'phrases.compat.global.other'}",
|
"other": "@:{'phrases.compat.global.other'}",
|
||||||
|
"overview": {
|
||||||
|
"active_vehicles": "Active vehicles",
|
||||||
|
"attributes": "Attributes",
|
||||||
|
"connected": "Connected",
|
||||||
|
"customer_management": "Customer management",
|
||||||
|
"customer_number_required": "A customer number is required before customer settings can be managed.",
|
||||||
|
"customer_rules": "Customer rules",
|
||||||
|
"effective_access": "Effective access",
|
||||||
|
"loaded": "Loaded",
|
||||||
|
"load_failed": "User data could not be loaded",
|
||||||
|
"loading_user": "Loading user",
|
||||||
|
"management_hub": "Management hub",
|
||||||
|
"missing": "Missing",
|
||||||
|
"more_permissions": "+{count} more permissions",
|
||||||
|
"no_subscription_transactions": "No wash subscription transactions are registered for this customer.",
|
||||||
|
"not_invoiced": "Not invoiced",
|
||||||
|
"open_draft": "Open draft",
|
||||||
|
"open_vehicles": "Open vehicles",
|
||||||
|
"price_overrides": "Price overrides",
|
||||||
|
"pricing": "Pricing",
|
||||||
|
"subscription_invoicing": "Subscription invoicing",
|
||||||
|
"subtitle": "Overview of customer data, access, pricing, vehicles, and subscription invoicing.",
|
||||||
|
"vehicles_load_failed": "Vehicles could not be loaded.",
|
||||||
|
"workspace_shortcuts": "Workspace shortcuts"
|
||||||
|
},
|
||||||
"permissions": "@.capitalize:{'terms.glossary.permissions'}",
|
"permissions": "@.capitalize:{'terms.glossary.permissions'}",
|
||||||
"user_data": "@.capitalize:{'terms.glossary.user'} @:{'terms.glossary.data'}",
|
"user_data": "@.capitalize:{'terms.glossary.user'} @:{'terms.glossary.data'}",
|
||||||
"user_id": "@.capitalize:{'terms.glossary.user'} @.upper:{'terms.glossary.id'}",
|
"user_id": "@.capitalize:{'terms.glossary.user'} @.upper:{'terms.glossary.id'}",
|
||||||
|
|||||||
@@ -2,6 +2,20 @@
|
|||||||
"compat": {
|
"compat": {
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"add": "@.capitalize:{'terms.glossary.add'} @:{'terms.glossary.vehicle'}",
|
"add": "@.capitalize:{'terms.glossary.add'} @:{'terms.glossary.vehicle'}",
|
||||||
|
"add_modal": {
|
||||||
|
"customer_label": "Customer",
|
||||||
|
"customer_placeholder": "Search by customer name or number",
|
||||||
|
"error": "Unable to add vehicle.",
|
||||||
|
"no_customer_results": "No customers found",
|
||||||
|
"reference_placeholder": "Optional reference",
|
||||||
|
"registration_placeholder": "Registration number",
|
||||||
|
"selected_customer": "Selected customer",
|
||||||
|
"submit": "Add vehicle",
|
||||||
|
"title": "Add vehicle",
|
||||||
|
"type_load_error": "Unable to load vehicle types.",
|
||||||
|
"type_placeholder": "Select vehicle type",
|
||||||
|
"validation_error": "Select a customer, registration number, and vehicle type."
|
||||||
|
},
|
||||||
"brand": "Brand",
|
"brand": "Brand",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"delete_vehicle": "@:{'phrases.compat.vehicles.delete'}",
|
"delete_vehicle": "@:{'phrases.compat.vehicles.delete'}",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user