diff --git a/.dockerignore b/.dockerignore index e0c9a46d6..92bd5419d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -40,7 +40,8 @@ composer.lock package.json package-lock.json gulpfile.js -.env.example +.env +.env.* .dockerignore # Temporary and backup files diff --git a/.env.example b/.env.example index 4a58682f9..d055638c1 100644 --- a/.env.example +++ b/.env.example @@ -60,6 +60,13 @@ database.tests.DBPrefix='ospos_' # ENCRYPTION_KEY is read as a fallback when encryption.key is empty, so no # shared key needs to be committed or baked into the shipped image. encryption.key='' +# Persistent secret for HMAC-hashing login-throttle cache keys. Left blank and +# provisioned on startup (php spark env:provision); independent of encryption.key. +# For Docker/Compose, pass it via the THROTTLE_KEY env var instead: +# docker run -e THROTTLE_KEY="$(openssl rand -hex 32)" opensourcepos +# THROTTLE_KEY is read as a fallback when throttle.key is empty, so no +# shared secret needs to be committed or baked into the shipped image. +throttle.key='' #-------------------------------------------------------------------- # LOGGER diff --git a/.github/workflows/README.md b/.github/workflows/README.md index ff51f5276..e4adb1f83 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -13,8 +13,10 @@ This document describes the CI/CD workflows for OSPOS. ### Docker Images - Build and push `opensourcepos` Docker image for multiple architectures (linux/amd64, linux/arm64) -- On master: tagged with version and `latest` -- On other branches: tagged with version only +- On `master`: tagged `master` and `` +- On other branches: tagged `-` +- On a semver tag (e.g. `3.4.2`): tagged `` and `latest` +- The version number is never stamped onto `master`/branch builds — it only appears on tag releases - Pushed to Docker Hub ### Releases @@ -39,10 +41,10 @@ The `GITHUB_TOKEN` is automatically provided by GitHub Actions. ## Workflow Triggers -- **Push to master** - Runs build, Docker push (with `latest` tag), and release -- **Push to other branches** - Runs build and Docker push (version tag only) -- **Push tags** - Runs build and Docker push (version tag only) -- **Pull requests** - Runs build only (PHPUnit tests run in parallel via phpunit.yml) +- **Push to master** - Runs build, Docker push (`master` + `` tags), and creates/updates the `unstable` release +- **Push to other branches** - Runs build and Docker push (`-` tag) +- **Push a semver tag** (e.g. `3.4.2`) - Runs build and Docker push (`` + `latest` tags) +- **Pull requests** - Runs build only (PHPUnit tests run in parallel via phpunit.yml); no Docker push ## Existing Workflows diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 23774385e..2aa1e3b96 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -2,6 +2,10 @@ name: Build and Release on: push: + branches: + - '**' + tags: + - '[0-9]+.[0-9]+.[0-9]+' pull_request: branches: - master @@ -86,6 +90,18 @@ jobs: env: GITHUB_TAG: ${{ github.ref_name }} + - name: Validate release tag + if: startsWith(github.ref, 'refs/tags/') + run: | + # The ZIP below is named from App.php, while the draft-release job + # globs by the tag name. A manually-pushed tag that does not match + # App.php would silently produce a draft release with no archive, so + # fail fast instead. + if [ "${GITHUB_REF_NAME}" != "${{ steps.version.outputs.version }}" ]; then + echo "::error::tag ${GITHUB_REF_NAME} does not match App.php version ${{ steps.version.outputs.version }}" + exit 1 + fi + - name: Create .env file run: | cp .env.example .env @@ -154,11 +170,19 @@ jobs: - name: Determine Docker tags id: tags run: | - BRANCH=$(echo "${GITHUB_REF#refs/heads/}" | tr '/' '_') - if [ "$BRANCH" = "master" ]; then - echo "tags=${{ secrets.DOCKER_USERNAME }}/opensourcepos:${{ needs.build.outputs.version-tag }},${{ secrets.DOCKER_USERNAME }}/opensourcepos:master" >> $GITHUB_OUTPUT + REGISTRY="${{ secrets.DOCKER_USERNAME }}/opensourcepos" + SHA="${{ needs.build.outputs.short-sha }}" + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + VERSION="${GITHUB_REF#refs/tags/}" + echo "tags=${REGISTRY}:${VERSION},${REGISTRY}:latest" >> "$GITHUB_OUTPUT" + elif [[ "$GITHUB_REF" == refs/heads/master ]]; then + echo "tags=${REGISTRY}:master,${REGISTRY}:${SHA}" >> "$GITHUB_OUTPUT" else - echo "tags=${{ secrets.DOCKER_USERNAME }}/opensourcepos:${{ needs.build.outputs.version-tag }}" >> $GITHUB_OUTPUT + BRANCH="${GITHUB_REF#refs/heads/}" + BRANCH="$(printf '%s' "$BRANCH" | LC_ALL=C tr -c 'A-Za-z0-9_.-' '_')" + BRANCH="${BRANCH:0:$((128 - ${#SHA} - 1))}" + [[ "$BRANCH" == [-.]* ]] && BRANCH="_${BRANCH:1}" + echo "tags=${REGISTRY}:${BRANCH}-${SHA}" >> "$GITHUB_OUTPUT" fi env: GITHUB_REF: ${{ github.ref }} @@ -172,8 +196,8 @@ jobs: push: true tags: ${{ steps.tags.outputs.tags }} - release: - name: Create Release + unstable-release: + name: Create Unstable Release needs: build runs-on: ubuntu-22.04 if: github.event_name == 'push' && github.ref == 'refs/heads/master' @@ -213,3 +237,77 @@ jobs: draft: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + official-release: + name: Create Official Release (Draft) + needs: [build, docker] + runs-on: ubuntu-22.04 + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist-${{ needs.build.outputs.short-sha }} + path: dist/ + + - name: Build release notes + run: | + VERSION="${GITHUB_REF_NAME}" + # Extract this version's changelog section (header + entries) from CHANGELOG.md. + SECTION=$(awk -v v="$VERSION" ' + $0 ~ "^## \\[" v "\\] - " {insec=1; print; next} + insec && $0 ~ "^## \\[" {insec=0; next} + insec {print} + ' CHANGELOG.md) + if [ -z "$SECTION" ]; then + echo "WARNING: no changelog section found for $VERSION" + SECTION="Changelog section for ${VERSION} is not present in CHANGELOG.md." + fi + { + echo "## Upgrade instructions" + echo "" + echo "**Docker:**" + echo "" + echo '```bash' + echo "docker pull ${{ secrets.DOCKER_USERNAME }}/opensourcepos:${VERSION}" + echo "docker compose -f docker-compose.nginx.yml up -d" + echo '```' + echo "" + echo "**Existing installation:** download one of the archives below, extract it over your current install, and run any new database migrations." + echo "" + echo "---" + echo "" + echo "$SECTION" + } > /tmp/release_notes.md + echo "=== release notes (first 12 lines) ===" + head -12 /tmp/release_notes.md + env: + GITHUB_REF_NAME: ${{ github.ref_name }} + + - name: Create draft release + run: | + VERSION="${GITHUB_REF_NAME}" + ZIP=$(ls dist/opensourcepos."${VERSION}".*.zip 2>/dev/null | head -1) + if [ -n "$ZIP" ]; then + gh release create "$VERSION" \ + --draft \ + --title "OpenSourcePOS ${VERSION}" \ + --notes-file /tmp/release_notes.md \ + "$ZIP" + else + gh release create "$VERSION" \ + --draft \ + --title "OpenSourcePOS ${VERSION}" \ + --notes-file /tmp/release_notes.md + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} diff --git a/.github/workflows/delete-unstable-release.yml b/.github/workflows/delete-unstable-release.yml index c10399599..c4247ed7c 100644 --- a/.github/workflows/delete-unstable-release.yml +++ b/.github/workflows/delete-unstable-release.yml @@ -8,15 +8,25 @@ on: jobs: delete_unstable_release: runs-on: ubuntu-latest - steps: - - name: "Delete last unstable release" - uses: sgpublic/delete-release-action@v1.2 - env: - GITHUB_TOKEN: ${{ secrets.TOKEN }} - with: - release-drop: false - release-drop-tag: false - pre-release-drop: true - pre-release-keep-count: -1 - pre-release-drop-tag: true - + steps: + - name: "Delete the unstable release (matched by name)" + env: + TOKEN: ${{ secrets.TOKEN }} + RELEASE_NAME: "Unstable OpenSourcePOS" + TAG_NAME: "unstable" + run: | + set -euo pipefail + api="https://api.github.com/repos/${GITHUB_REPOSITORY}" + # Resolve the release id from its exact name so only this one release + # is ever touched; drafts and every other release are left alone. + id=$(curl -fsSL -H "Authorization: Bearer ${TOKEN}" "${api}/releases?per_page=100" \ + | jq -r --arg n "${RELEASE_NAME}" '.[] | select(.name==$n) | .id // empty') + if [ -z "${id}" ]; then + echo "No release named '${RELEASE_NAME}' found; nothing to do." + exit 0 + fi + curl -fsSL -X DELETE -H "Authorization: Bearer ${TOKEN}" "${api}/releases/${id}" >/dev/null + echo "Deleted release '${RELEASE_NAME}' (id=${id})" + curl -fsSL -X DELETE -H "Authorization: Bearer ${TOKEN}" "${api}/git/refs/tags/${TAG_NAME}" >/dev/null \ + || echo "Tag '${TAG_NAME}' already gone; skipping." + echo "Deleted tag '${TAG_NAME}'" diff --git a/.github/workflows/deploy-pr.yml b/.github/workflows/deploy-pr.yml index d13e40c47..ec9751698 100644 --- a/.github/workflows/deploy-pr.yml +++ b/.github/workflows/deploy-pr.yml @@ -54,7 +54,7 @@ jobs: comment: name: Comment deployment status needs: [prepare, deploy] - if: always() + if: always() && needs.prepare.result == 'success' runs-on: ubuntu-latest env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index dd20ae4dc..b4c69e499 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -118,6 +118,22 @@ jobs: KEY=$(openssl rand -hex 32) printf 'ENCRYPTION_KEY=%s\n' "$KEY" >> "$GITHUB_ENV" + - name: Provision per-run throttle key + # checkThrottleEncryption() is a read-only guard that throws unless a + # throttle.key is present (app/Helpers/security_helper.php). It is + # normally minted at container startup via `php spark env:provision`, + # so the test environment must provide one. + # + # We write it into the .env file (replacing the empty placeholder + # copied from .env.example) rather than exporting an OS env var: + # CodeIgniter's env() resolves $_ENV first, and DotEnv populates + # $_ENV['throttle.key']='' from .env, which would shadow any OS var + # before getenv() is ever consulted. + run: | + KEY=$(openssl rand -hex 32) + sed -i "s/^throttle\.key=''/throttle.key='$KEY'/" .env + grep -Eq "^throttle\.key=.+" .env + - name: Run PHPUnit tests env: CI_ENVIRONMENT: testing diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de5582582..c9f306ce8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,24 +1,40 @@ -name: Release Version Bump +name: Release + +# Cuts the current release (changelog + tag + draft release) and, optionally, +# bumps App.php to the next dev version. One workflow, full release cycle. +# +# Flow: +# 1. Release current version: +# - generate git-cliff changelog section (base..HEAD) +# - insert it into CHANGELOG.md + GFM compare refs +# - commit + push the changelog +# - tag the current version and push the tag with the repo PAT +# (a PAT push triggers build-release.yml, whose official-release job +# creates the DRAFT GitHub Release with the changelog + assets) +# 2. (optional) bump to the next dev version: +# - update App.php / package.json / docker-compose / issue templates +# - commit + push on: workflow_dispatch: inputs: - version_type: - description: 'Version bump type' + next_bump: + description: 'After cutting this release, bump App.php to the next dev version' required: true type: choice options: + - patch - minor - major - - patch - default: 'minor' + - skip + default: 'patch' permissions: contents: write jobs: - prepare-release: - name: Prepare Release + release: + name: Cut release and bump next version runs-on: ubuntu-22.04 steps: @@ -26,147 +42,124 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} + # Use the repo PAT (an admin token) so the changelog + bump pushes + # below can update the protected master branch (enforce_admins is off). + # GITHUB_TOKEN cannot, because master requires a PR review. + token: ${{ secrets.TOKEN }} - - name: Get current version - id: current_version + - name: Install git-cliff run: | - CURRENT_VERSION=$(grep "application_version" app/Config/App.php | sed "s/.*= '\(.*\)';/\1/g") - echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - echo "Current version: $CURRENT_VERSION" + V=2.14.2 + curl -sLSfL "https://github.com/orhun/git-cliff/releases/download/v${V}/git-cliff-${V}-x86_64-unknown-linux-musl.tar.gz" -o /tmp/cliff.tar.gz + tar xzf /tmp/cliff.tar.gz -C /tmp + mv "/tmp/git-cliff-${V}/git-cliff" /usr/local/bin/git-cliff + chmod +x /usr/local/bin/git-cliff + git cliff --version - - name: Calculate new version + - name: Determine version id: version run: | - CURRENT_VERSION="${{ steps.current_version.outputs.current_version }}" - VERSION_TYPE="${{ github.event.inputs.version_type }}" - - # Parse current version - MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1) - MINOR=$(echo $CURRENT_VERSION | cut -d. -f2) - PATCH=$(echo $CURRENT_VERSION | cut -d. -f3) - - # Bump version based on type - case $VERSION_TYPE in - major) - MAJOR=$((MAJOR + 1)) - MINOR=0 - PATCH=0 - ;; - minor) - MINOR=$((MINOR + 1)) - PATCH=0 - ;; - patch) - PATCH=$((PATCH + 1)) - ;; - esac - - NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}" - echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT - echo "previous_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - echo "New version: $NEW_VERSION (was: $CURRENT_VERSION, type: $VERSION_TYPE)" - - - name: Update version in App.php - run: | - NEW_VERSION="${{ steps.version.outputs.new_version }}" - sed -i "s/public string \\\$application_version = '[^']*';/public string \\\$application_version = '$NEW_VERSION';/" app/Config/App.php - echo "Updated app/Config/App.php" - - - name: Update version in package.json - run: | - NEW_VERSION="${{ steps.version.outputs.new_version }}" - sed -i "s/\"version\": \"[^\"]*\",/\"version\": \"$NEW_VERSION\",/" package.json - echo "Updated package.json" - - - name: Update version in docker-compose.nginx.yml - run: | - NEW_VERSION="${{ steps.version.outputs.new_version }}" - sed -i "s/jekkos\/opensourcepos:[^ ]*/jekkos\/opensourcepos:$NEW_VERSION/" docker-compose.nginx.yml - echo "Updated docker-compose.nginx.yml" - - - name: Update version in README.md - run: | - NEW_VERSION="${{ steps.version.outputs.new_version }}" - # Extract major.minor for the "latest X.Y version" text - MAJOR_MINOR=$(echo "$NEW_VERSION" | cut -d. -f1,2) - sed -i "s/The latest \`[0-9]*\.[0-9]*\` version/The latest \`${MAJOR_MINOR}\` version/" README.md - echo "Updated README.md with version ${MAJOR_MINOR}" - - - name: Generate changelog - id: changelog - run: | - PREVIOUS_VERSION="${{ steps.version.outputs.previous_version }}" - NEW_VERSION="${{ steps.version.outputs.new_version }}" - - # Get commits since last version - if git rev-parse "$PREVIOUS_VERSION" >/dev/null 2>&1; then - COMMITS=$(git log "$PREVIOUS_VERSION"..HEAD --pretty=format:"- %s" --no-merges) - else - COMMITS=$(git log --pretty=format:"- %s" --no-merges -50) + APP_VERSION=$(grep "application_version" app/Config/App.php | sed "s/.*= '\(.*\)';/\1/g") + TARGET="$APP_VERSION" + if ! [[ "$TARGET" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "ERROR: target '$TARGET' is not in X.Y.Z form"; exit 1 fi - - # Create changelog entry - CHANGELOG_FILE="CHANGELOG.md" - - # Create the new version comparison link - NEW_LINK="[${NEW_VERSION}]: https://github.com/opensourcepos/opensourcepos/compare/${PREVIOUS_VERSION}...${NEW_VERSION}" - - # Insert new link after [unreleased] line - sed -i "/^\[unreleased\]/a $NEW_LINK" "$CHANGELOG_FILE" - - # Update [unreleased] link to start from new version - sed -i "s|^\[unreleased\]: .*|\[unreleased\]: https://github.com/opensourcepos/opensourcepos/compare/${NEW_VERSION}...HEAD|" "$CHANGELOG_FILE" - - # Create version header and content using temp file to avoid sed issues with special characters - VERSION_DATE=$(date +%Y-%m-%d) - VERSION_HEADER="## [$NEW_VERSION] - $VERSION_DATE" - - # Create temp file with changelog entry - TMP_FILE=$(mktemp) - { - echo "" - echo "$VERSION_HEADER" - echo "" - echo "$COMMITS" - } > "$TMP_FILE" - - # Insert after Unreleased header - sed -i "/^## \[Unreleased\]/r $TMP_FILE" "$CHANGELOG_FILE" - rm "$TMP_FILE" - - echo "Updated CHANGELOG.md" - echo "Changelog entries:" - echo "$COMMITS" + BASE=$(git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1) + if [ -z "$BASE" ]; then + echo "ERROR: no previous release tag found"; exit 1 + fi + if [ "$BASE" = "$TARGET" ]; then + echo "ERROR: target $TARGET equals base $BASE; nothing to release"; exit 1 + fi + if ! printf '%s\n%s\n' "$BASE" "$TARGET" | sort -VC 2>/dev/null; then + echo "ERROR: target $TARGET is not greater than base $BASE"; exit 1 + fi + echo "target=$TARGET" >> "$GITHUB_OUTPUT" + echo "base=$BASE" >> "$GITHUB_OUTPUT" + echo "Releasing $TARGET (previous release: $BASE)" - - name: Update version in issue templates + - name: Generate changelog section run: | - NEW_VERSION="${{ steps.version.outputs.new_version }}" - - # Calculate version to remove (keep 5 versions) - PREVIOUS_VERSION="${{ steps.version.outputs.previous_version }}" - - # Bug report template - insert new version after development (unreleased) - BUG_TEMPLATE=".github/ISSUE_TEMPLATE/bug report.yml" - sed -i "/- development (unreleased)/a\\ - OpenSourcePOS ${NEW_VERSION}" "$BUG_TEMPLATE" - # Remove the oldest version (5th version from the end) - sed -i "/OpenSourcePOS 3\\.3\\.7/d" "$BUG_TEMPLATE" - echo "Updated $BUG_TEMPLATE" - - # Feature request template - insert new version after development (unreleased) - FEATURE_TEMPLATE=".github/ISSUE_TEMPLATE/feature_request.yml" - sed -i "/- development (unreleased)/a\\ - OpenSourcePOS ${NEW_VERSION}" "$FEATURE_TEMPLATE" - # Remove the oldest version (5th version from the end) - sed -i "/OpenSourcePOS 3\\.3\\.7/d" "$FEATURE_TEMPLATE" - echo "Updated $FEATURE_TEMPLATE" + git cliff --config cliff.toml \ + --tag "${{ steps.version.outputs.target }}" \ + --output /tmp/section.md \ + "${{ steps.version.outputs.base }}..HEAD" + sed -i '/./,$!d' /tmp/section.md + echo "Generated $(grep -c '^- ' /tmp/section.md) entries" + echo "=== first 5 ===" + head -5 /tmp/section.md - - name: Commit version bump + - name: Insert into CHANGELOG.md + run: | + TARGET="${{ steps.version.outputs.target }}" + BASE="${{ steps.version.outputs.base }}" + sed -i "/^\[unreleased\]:/a [${TARGET}]: https://github.com/opensourcepos/opensourcepos/compare/${BASE}...${TARGET}" CHANGELOG.md + sed -i "s|^\[unreleased\]:.*|\[unreleased\]: https://github.com/opensourcepos/opensourcepos/compare/${TARGET}...HEAD|" CHANGELOG.md + LINE=$(grep -nE '^## \[[0-9]+\.' CHANGELOG.md | head -1 | cut -d: -f1) + printf '%s\n\n' "$(cat /tmp/section.md)" > /tmp/section_final.md + sed -i "$((LINE-1))r /tmp/section_final.md" CHANGELOG.md + echo "Inserted $TARGET section before line $LINE" + + - name: Commit changelog run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - NEW_VERSION="${{ steps.version.outputs.new_version }}" - - git add app/Config/App.php package.json docker-compose.nginx.yml CHANGELOG.md README.md .github/ISSUE_TEMPLATE/ - git commit -m "chore: release version $NEW_VERSION" - git push origin HEAD \ No newline at end of file + git add CHANGELOG.md + git commit -m "docs: add ${{ steps.version.outputs.target }} changelog" + git push origin HEAD + + - name: Tag and push (PAT triggers build-release.yml) + run: | + TAG="${{ steps.version.outputs.target }}" + git tag "$TAG" + # Push with the repo PAT (not GITHUB_TOKEN) so the tag-push event + # triggers build-release.yml, whose official-release job creates the + # draft release with the changelog and assets. + git push "https://x-access-token:${RELEASE_PAT}@github.com/opensourcepos/opensourcepos.git" "refs/tags/$TAG" + echo "Released $TAG — draft release will be created by build-release.yml" + env: + RELEASE_PAT: ${{ secrets.TOKEN }} + + - name: Bump to next dev version (optional) + if: ${{ inputs.next_bump != 'skip' }} + run: | + CURRENT="${{ steps.version.outputs.target }}" + MAJOR=$(echo "$CURRENT" | cut -d. -f1) + MINOR=$(echo "$CURRENT" | cut -d. -f2) + PATCH=$(echo "$CURRENT" | cut -d. -f3) + case "${{ inputs.next_bump }}" in + major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; + minor) MINOR=$((MINOR + 1)); PATCH=0 ;; + patch) PATCH=$((PATCH + 1)) ;; + *) echo "ERROR: unknown bump type ${{ inputs.next_bump }}"; exit 1 ;; + esac + NEXT="${MAJOR}.${MINOR}.${PATCH}" + echo "Bumping to next dev version $NEXT" + + sed -i "s/public string \\\$application_version = '[^']*';/public string \\\$application_version = '$NEXT';/" app/Config/App.php + sed -i "s/\"version\": \"[^\"]*\",/\"version\": \"$NEXT\",/" package.json + # Keep package-lock.json in sync: bump only the @opensourcepos/opensourcepos + # package version (top-level + packages.""), leave dependency versions alone. + awk -v v="$NEXT" ' + /"name": "@opensourcepos\/opensourcepos"/ { expect=1 } + expect && /"version": / { sub(/"version": "[^"]*"/, "\"version\": \"" v "\""); expect=0 } + { print } + ' package-lock.json > .package-lock.tmp && mv .package-lock.tmp package-lock.json + # Update the "latest X.Y version" README line only when major.minor changes + NEW_MM=$(echo "$NEXT" | cut -d. -f1,2) + OLD_MM=$(echo "$CURRENT" | cut -d. -f1,2) + if [ "$NEW_MM" != "$OLD_MM" ]; then + sed -i "s/The latest \`[0-9]*\.[0-9]*\` version/The latest \`${NEW_MM}\` version/" README.md + fi + + echo "=== version refs after bump ===" + grep "application_version" app/Config/App.php + grep '"version"' package.json | head -1 + grep -A1 '"name": "@opensourcepos/opensourcepos"' package-lock.json + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add app/Config/App.php package.json package-lock.json + [ "$NEW_MM" != "$OLD_MM" ] && git add README.md + git commit -m "chore: bump version to $NEXT" + git push origin HEAD diff --git a/AGENTS.md b/AGENTS.md index 4f79fa75d..d423d2acd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ This document provides guidance for AI agents working on the Open Source Point o - PHP 8.2+ features acceptable (named arguments, enums, readonly properties) - Write PHP 8.2+ compatible code with proper type declarations - Always import classes, functions, and constants with a `use` statement at the top of the file instead of referencing them inline via fully-qualified name (e.g. `use Config\Database;` then `Database::connect()`, not `\Config\Database::connect()`) -- Do not add comments or docblocks that merely restate what the code already makes clear — only comment on non-obvious rationale, constraints, or behavior +- No useless comments or docblocks: if the code speaks for itself, explanatory comments are unnecessary. Do not add comments or docblocks that merely restate what the code already makes clear (e.g. `@param array $items` next to `array $items`, or a comment repeating a method's name) — only comment on non-obvious rationale, constraints, or behavior - Views in `app/Views/errors/html/` are excluded from the fixer - Run fixer before committing: `vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.no-header.php` - **JavaScript**: use `const` for variables that are never reassigned, `let` for variables that are. Never use `var`. @@ -27,6 +27,7 @@ This document provides guidance for AI agents working on the Open Source Point o - Run PHPUnit tests: `composer test` - Tests must pass before submitting changes - **One test file per class under test.** A controller, model, library, or helper gets exactly one test file covering all of its behavior — `Item_kits.php` → `tests/Controllers/Item_kitsTest.php` (or `Item_kitsControllerTest.php`, matching this codebase's existing `*ControllerTest.php` suffix for controllers), `Sale_lib.php` → `tests/Libraries/Sale_libTest.php`, etc. Do not create feature- or endpoint-scoped test files alongside a class's main test file (e.g. no `Item_kitsBarcodeTest.php` next to `Item_kitsControllerTest.php`) — add the new test methods to the existing file for that class instead. If no test file exists yet for the class, create the one canonical file rather than a narrowly-scoped one. +- **Name test methods as `test{MethodUnderTest}_{Behavior}`.** `PascalCase` the method under test, then an underscore, then a short behavior phrase — `testPostSave_RejectsNegativeTaxPercent`, `testSaveLocale_AcceptsValidReferenceCodeMinMax`. Start the behavior with a verb: `Accepts`/`Requires` for valid or boundary cases, `Rejects` for invalid ones. The name should read as a self-documenting sentence and needs no docblock. ## Build @@ -50,6 +51,7 @@ This document provides guidance for AI agents working on the Open Source Point o - **When explicitly asked to translate a phrase for a non-English language file, always provide the actual translation** — never leave the value as an empty string, and never leave source English text in a non-English language file - Never copy English text from a neighboring key as a value for a non-English language file, even if that neighboring key is already untranslated — evaluate each key independently - Only `app/Language/en/` and `app/Language/en-GB/` should contain English strings +- When translating a string containing placeholders (e.g. `{filePath}`, `{reason}`) or literal filenames/keys (e.g. `throttle.key`, `.env`), keep that placeholder or filename text unchanged and untranslated — move it to whatever position is grammatically correct in the translated sentence - Plugin language files (`app/Plugins/*/Language/`) follow the same localization rules as `app/Language/` - Use `'` to encapsulate key and string values. If the value contains `'` then it should be escaped as `\'` - Align the `=>` of a newly inserted key with the `=>` column already used by the rest of the file, if that file pads keys to a fixed column (not all do — some files have no padding at all). If the new key is longer than the widest existing key and would push the alignment column further right, reformat the whole file to the new wider column rather than leaving only the new line at a different width @@ -59,3 +61,4 @@ This document provides guidance for AI agents working on the Open Source Point o - Never commit secrets, credentials, or `.env` files - Use parameterized queries to prevent SQL injection - Validate and sanitize all user input +- Never reference security advisory IDs (CVE, GHSA, etc.) in code, comments, commit messages, docblocks, documentation, or URLs — treat them the same as secrets. They act as a roadmap for attackers researching the exact exploit a fix addresses. diff --git a/BUILD.md b/BUILD.md index b3858453c..57dd3efc4 100644 --- a/BUILD.md +++ b/BUILD.md @@ -14,6 +14,7 @@ The build process uses the build tools "npm" and "gulp" to piece everything toge ## Prerequisites +- Install Node.js 20 or later (the build fails on Node 18 and earlier - one of the license reporting dependencies requires newer JavaScript regex features) - Install the latest version of NPM (tested using version 9.4.2) - Install the latest version of Composer (tested using composer 2.5.1) diff --git a/CHANGELOG.md b/CHANGELOG.md index 407c8b711..890a137b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ -[unreleased]: https://github.com/opensourcepos/opensourcepos/compare/3.4.1...HEAD +[unreleased]: https://github.com/opensourcepos/opensourcepos/compare/3.4.2...HEAD +[3.4.2]: https://github.com/opensourcepos/opensourcepos/compare/3.4.1...3.4.2 [3.4.1]: https://github.com/opensourcepos/opensourcepos/compare/3.4.0...3.4.1 [3.4.0]: https://github.com/opensourcepos/opensourcepos/compare/3.3.9...3.4.0 [3.3.9]: https://github.com/opensourcepos/opensourcepos/compare/3.3.8...3.3.9 @@ -33,6 +34,243 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [3.4.2] - 2026-09-30 +- Fix writable folder permission check (#4270) (#4273) by @jekkos +- Extended payment delete fix (#4274) by @jekkos +- Upgrade github workflow (#3708) (#4280) by @jekkos +- Fix typo in writeable (#4270) by @jekkos +- Fix migration 20250522000000 (#4284) by @jekkos +- Upgrade to ci 4.6.2 (#4296) (#4298) by @jekkos +- Fix barcode generation in items (#4270) by @jekkos +- Allow empty tax category id (#4285) (#4288) by @jekkos +- Fix security incident email address (#4298) by @jekkos +- Fix item kits update (#4294) by @jekkos +- Revert toast message sanitization (#4302) by @jekkos +- Fix for suspended sales (#4283) (#4303) by @jekkos +- Fix reference to uploads folder (#4270) (#4286) by @jekkos +- Add generic try/catch in import (#4302) by @jekkos +- Bump jspdf from 3.0.1 to 3.0.2 (#4309) by @dependabot[bot] +- Fix mount path for uploads (#4308) by @jekkos +- Add transactions to missing config keys migration. (#4318) by @Joe Williams +- [Feature] Add logging to migrations (#4327) by @Joe Williams +- Clean up docker compose setup (#4308) by @jekkos +- Fix tax configuration pages (#4331) by @jekkos +- Update SECURITY.md contact (#4335) by @jekkos +- Add server side validation for password (#4335) by @jekkos +- Add env variable to disallow pwd change (#4325) by @jekkos +- Add recent releases to issue template (#4317) by @jekkos +- Add DOMpurify + fix XSS (#4341) by @jekkos +- Fix attachment cid (#4314) by @jekkos +- Add DOMPurify to JS includes (#4341) by @jekkos +- Allow anonymous giftcard creation (#4278) by @jekkos +- Fix toast notifications in config (#4341) (#4343) by @jekkos +- Fix creation of date attribute value (#4310) (#4344) by @jekkos +- Fix wrong migration script location (#4285) by @jekkos +- Escape return_policy in receipt + invoice (#4349) by @jekkos +- Fix for detailed suppliers report (#4351) by @jekkos +- Add show/hide cost price & profit feature - in reports #4130 (#4350) by @BhojKamal +- Fix travis build after merge (#4130) by @jekkos +- Add equals as permitted URI character (#4329) by @Chathura Dilushanka +- Fix multiple XSS vulnerabilities (#3965) (#4356) by @jekkos +- Bump lodash from 4.17.21 to 4.17.23 (#4369) by @dependabot[bot] +- Bump jspdf and jspdf-autotable (#4373) by @dependabot[bot] +- Fix XSS vulnerabilities in invoices + receipts (#3965) (#4363) by @jekkos +- Fix XSS vulnerability in attributes (#3965) by @jekkos +- Fix XSS vulnerability in register (#3965) by @jekkos +- Fix XSS vulnerability in register (#3965) by @jekkos +- Fix XSS vulnerabilities in invoice_email.php view by @jekkos +- Fix permission bypass in Reports submodule access control (#4389) by @jekkos +- Use Content-Type application/json for AJAX responses (#4357) by @jekkos +- Language Array Key Typo Fix (#4371) by @Lucas Lyimo +- Fix: Refresh session language for employee after update. (#4245) by @jekkos +- Fix Docker image upload by replacing slashes in TAG by @jekkos +- Fix broken object-level authorization in Employees controller (CVE-worthy) (#4391) by @jekkos +- Bump dompurify from 3.3.1 to 3.3.2 (#4402) by @dependabot[bot] +- Fix incorrect argument types in migration round_number() methods (#4403) by @jekkos +- dd validation for invalid stock locations in CSV import (#4399) by @jekkos +- fix(security): whitelist and validate invoice template types (#4393) by @jekkos +- Fix second-order SQL injection in currency_symbol config (#4390) by @jekkos +- Add row-level authorization to password change endpoints (#4401) by @jekkos +- Fix: Handle image filenames with spaces in thumbnails by @jekkos +- Fix: Sanitize image filenames to prevent thumbnail display issues (#4372) by @jekkos +- Add migration to fix existing image filenames with spaces (#4372) by @jekkos +- Refactor: Move ADMIN_MODULES to constants, rename methods to camelCase by @jekkos +- Fix SQL injection in custom attribute search by @Ollama +- Fix stored XSS vulnerability in item descriptions by @Ollama +- Fix stored XSS vulnerabilities in employee permissions and customer data by @Ollama +- Fix: Preserve CHECKBOX attribute state when adding attributes (#4385) by @jekkos +- Fix payment type becoming null when editing sales by @Ollama +- Fix broken SQL injection fix - use havingLike() instead of having() with named params by @Ollama +- Fix mass assignment vulnerability in bulk edit (GHSA-49mq-h2g4-grr9) by @Ollama +- Sync language files (#3468) by @Ollama +- Add workflow to auto-update issue templates with releases by @Ollama +- Update SECURITY.md with published security advisories by @Ollama +- Bump jspdf from 4.1.0 to 4.2.0 (#4383) by @dependabot[bot] +- Add filter persistence for table views via URL query string (#4400) by @jekkos +- Fix filter persistence javascript issues (#4400) by @jekkos +- Fix PHPUnit test configuration for database connectivity (#4430) by @jekkos +- Fix IDOR vulnerability in password change (GHSA-mcc2-8rp2-q6ch) (#4427) by @jekkos +- Fix XSS vulnerability in tax invoice view (#4432) by @jekkos +- Fix permission bypass in Sales.getManage() access control (#4428) by @jekkos +- Update SECURITY.md with published security advisories (#4431) by @jekkos +- Fix SQL injection in suggestions column configuration (#4421) by @jekkos +- Fix PHPUnit environment variables not being set (#4434) by @jekkos +- Fix DECIMAL attribute not respecting locale format (#4422) by @jekkos +- Fix stored XSS vulnerability in Attribute Definitions (GHSA-rvfg-ww4r-rwqf) (#4429) by @jekkos +- Fix: Host Header Injection vulnerability (GHSA-jchf-7hr6-h4f3) by @Ollama +- Fix stored XSS in gcaptcha_site_key on login page by @Ollama +- Fix stored XSS via stock location name by @Ollama +- Fix Token_lib::render() for PHP 8.4 compatibility by @Ollama +- Use CIUnitTestCase for consistency with other tests by @Ollama +- Fix: Pass parameter to generate() and add composite format tests by @Ollama +- Fix strftime directives handling and tighten test assertions by @Ollama +- Add AGENTS.md with coding guidelines for AI agents by @Ollama +- Fix: Add Debit Card filter to Daily Sales and Takings by @Ollama +- Fix Taxes Summary Report totals not matching row values by @Ollama +- Add unit tests for Taxes Summary Report calculations by @Ollama +- Fix rounding consistency and update tests per review feedback by @Ollama +- Rewrite tests to use database integration testing by @Ollama +- Add seed data to tests for proper integration testing by @Ollama +- Fix: Restrict employee selection in expenses and receivings forms by @Ollama +- Fix review comments: remove redundant loop and add XSS escaping by @Ollama +- Bump jspdf from 4.2.0 to 4.2.1 by @dependabot[bot] +- Bump picomatch from 2.3.1 to 2.3.2 (#4451) by @dependabot[bot] +- fix: Clear sale session after completing sale by @Ollama +- fix: Remove redundant clear_mode() calls by @Ollama +- Translate missing strings in multiple languages by @Ollama +- Fix translation issues from code review by @Ollama +- Remove English fallbacks from non-English translations by @Ollama +- Add Calendar.php translations for missing languages by @Ollama +- feat: migrate CI from Travis to GitHub Actions with enhancements by @Ollama +- refactor: remove tables.sql and constraints.sql (#4447) by @Ollama +- refactor: remove build-database gulp task (#4447) by @Ollama +- refactor: optimize Docker image size by @Ollama +- fix: remove duplicate phpunit.xml that prevented tests from running by @Ollama +- fix: Use file-based session until database is migrated by @Ollama +- feat: Improve migration UX on login page by @Ollama +- Disable opencode workflow + run docker build by @jekkos +- Fix negative price/quantity/discount validation (GHSA-wv3j-pp8r-7q43) (#4450) by @Nozomu Sasaki (Paul) +- fix(ci): replace / with _ in branch names for Docker tags by @Ollama +- fix(security): prevent command injection in sendmail path configuration by @Ollama +- fix(security): prevent SQL injection in tax controller sort columns by @Ollama +- feat: add release workflow with automated version bumping by @Ollama +- refactor: simplify release workflow to version bump only by @Ollama +- fix: address review comments by @Ollama +- fix: address all review comments and restore issue template version update by @Ollama +- fix: Tax Rate form not loading due to router service failure (#4479) by @jekkos +- fix: Handle empty database on fresh install (#4467) by @jekkos +- Fix: Improve allowedHostnames .env configuration and fail-fast in production (#4482) by @jekkos +- [Feature]: Case-sensitive attribute updates and CSV Import attribute deletion capability (#4384) by @objecttothis +- fix: change docker image tag to master by @jekkos +- Update to CodeIgniter 4.7.2 (#4485) by @objecttothis +- Bump lodash from 4.17.23 to 4.18.1 (#4462) by @dependabot[bot] +- [Fix]: Add missing return statements to Sales Controller functions by @Ollama +- Encourage users to star the project by @objecttothis +- Bump dompurify from 3.3.2 to 3.4.0 (#4512) by @dependabot[bot] +- fix: propagate attribute definition failures in postSaveGeneral() (#4509) by @jekkos +- fix: Escape dynamic output and fix CSS property in barcode_sheet.php (#4501) by @jekkos +- Fix CRC currency reverting to EUR/LAK in locale config (#4511) by @jekkos +- fix: Add missing $img_tag variable in Sales::getSendPdf() (#4515) by @jekkos +- fix: Language dropdown not displaying saved language correctly (#4518) by @jekkos +- fix: Scope orWhere clauses in Item::exists() and Item::get_item_id() (#4520) by @jekkos +- fix: Update calendar translations (#4498) by @jekkos +- fix: Catch mysqli_sql_exception in DB fallback handlers for fresh Docker installs (#4525) by @jekkos +- fix(home): improve internal data type handling for user identification in auth process by @enricodelarosa +- Assignable Keyboard Shortcuts Updates (#4532) by @WShells +- chore: miscellaneous updates and improvements (#4530) by @BudsieBuds +- chore(deps): bump minimatch from 3.1.2 to 3.1.5 (#4536) by @dependabot[bot] +- chore: sync project files to match upstream templates (#4537) by @BudsieBuds +- fix(ci): include hidden files in Docker build context (#4543) by @jekkos +- feat: add ALLOWED_HOSTNAMES environment variable support for Docker/Compose (#4544) by @jekkos +- fix(docker): correct permissions and fix migration barcode_type error (#4546) by @jekkos +- docs: Update SECURITY.md with disclosure process (#4549) by @jekkos +- feat: Bank transfer and wallet payment option added #4540 (#4547) by @BhojKamal +- fix(security): Path traversal vulnerability in getPicThumb (#4545) by @jekkos +- fix(security): SQL injection and path traversal vulnerabilities (#4539) by @jekkos +- fix: Capture CSV import failures in save_tax_data and save_inventory_quantities (#4507) by @jekkos +- fix: validate attributeId > 0 in saveAttributeLink() (#4508) by @jekkos +- feat: Add deployment workflow with approval gates (#4522) by @jekkos +- Bugfixes to get Migration working on MySQL and MariaDB (#4551) by @objecttothis +- Bugfix: Sale search in register not handling trailing space properly (#4557) by @objecttothis +- fix: cast string returns to int in MY_Migration (#4560) by @jekkos +- Add fallback for allowedHostnames environment variable (#4565) by @objecttothis +- fix: Allow searching by Sale ID in Takings/Daily Sales view (#4569) by @jekkos +- Add Guards to Database Migration (#4571) by @objecttothis +- fix: tax rate inputs blank with comma-decimal locales (#4555) by @jekkos +- fix(security): Fix DOMPDF RCE and customer email sanitization (#4568) by @jekkos +- Fix overly lenient date validation (#4574) by @objecttothis +- fix(security): Escape attribute value in register by @jekkos +- chore(deps): bump dompurify from 3.4.0 to 3.4.11 (#4578) by @dependabot[bot] +- Bugfix: Fix problems with migration UI in login (#4589) by @objecttothis +- Forgotten commit from login migration branch (#4592) by @objecttothis +- Feature: Payment reference code (#4587) by @objecttothis +- fix(giftcard): correct return type and rename getGiftcardId method (#4600) by @objecttothis +- chore(deps): bump dompurify from 3.4.11 to 3.4.12 (#4602) by @dependabot[bot] +- bugfix(reports): crash on detailed sales report when sale has multiple payments with reference codes (#4599) by @objecttothis +- style(models): normalize quote style in SQL GROUP_CONCAT expression (#4608) by @objecttothis +- chore(deps): upgrade dompdf from v2.0.8 to v3.1.6 (#4610) by @objecttothis +- chore(deps): bump brace-expansion (#4614) by @dependabot[bot] +- chore(deps): add xlsx via SheetJS CDN and upgrade tableexport plugin (#4615) by @objecttothis +- chore(deps): bump lodash.template from 4.5.0 to 4.18.1 (#4616) by @objecttothis +- fix(login): skip auth validation on new install to allow migration (#4609) by @objecttothis +- fix: Wrap postSave() in single transaction for atomicity (#4506) by @jekkos +- refactor: Replace var with let/const in JavaScript files (#4503) by @jekkos +- fix: get_definition_by_name() returns single row instead of multi-dimensional array (#4452) (#4464) by @Jonathan Chang +- fix(config): validate theme param to prevent XSS via invalid theme (#4620) by @objecttothis +- fix(sales): enforce server-side authorization for price changes (#4631) by @objecttothis +- fix(sales): escape quote number in email template to prevent XSS (#4625) by @objecttothis +- refactor(migrations): rename execute_script to executeScript across all migrations (#4611) by @objecttothis +- fix(auth): validate gcaptcha before password to prevent bypass (#4618) by @objecttothis +- refactor: apply PSR-12 naming to Attribute definition methods (#4624) by @Rayan Abdul Cader +- fix(security): sanitize filenames and escape logo path in config (#4630) by @objecttothis +- fix: use db_connect() for item save transactions (#4636) by @richardmilles +- fix(xss): remove redundant escaping that double-encoded item attribute values (#4628) by @objecttothis +- chore(deps): bump codeigniter4/framework from 4.7.2 to 4.7.4 (#4638) by @dependabot[bot] +- chore(deps): bump dompurify from 3.4.12 to 3.4.13 (#4639) by @dependabot[bot] +- Reject item CSV imports whose header row is missing required columns (#4597) by @Sai Asish Y +- fix(items): validate item_number and skip receiving quantity default for temp items (#4621) by @objecttothis +- Feature: CodeIgniter Throttler (#4619) by @objecttothis +- Bugfix: Resolve Race Condition in Rewards and Gift Card Spending (#4640) by @objecttothis +- hotfix(auth): hash throttler keys to improve security (#4646) by @objecttothis +- fix(items): add explicit sentinel value for clearing supplier in bulk edit (#4617) by @objecttothis +- Codeigniter changes between 4.7.2 and 4.7.4 (#4650) by @objecttothis +- Hotfix: Fix CI3 database migration caused by regression (#4649) by @objecttothis +- fix(sales): gate per-record endpoints behind reports_sales grant (#4627) by @objecttothis +- fix(email): update method call to camelCase for PSR-12 compliance (#4659) by @objecttothis +- Feature admin account safeguards (#4657) by @objecttothis +- Ensure payload data is escaped to prevent XSS (#4664) by @objecttothis +- fix(sales): enforce reports_sales grant on search endpoint (#4673) by @objecttothis +- fix(reports, home): resolve double-URL-decoding bypass for method grants (#4660) (#4666) by @objecttothis +- Bugfix tax names (#4677) by @objecttothis +- feat(validation, tests): add `valid_path_strict` rule and integrate into mailpath validation (#4684) by @objecttothis +- fix(sales): harden payment validation and gift card handling by @objecttothis +- fix: prevent duplicate items when editing imported rows (#4634) by @richardmilles +- fix(barcode): resolve string interpolation issue in barcode display html (#4692) by @Vighnesh Nilajakar +- fix(sales): gate getSearch behind reports_sales grant by @jekkos +- bugfix(sales): reject non-negative gift-card amount_tendered (#4674) by @jekkos +- fix(sales): harden unsuspend with auth, status gating, and null safety by @objecttothis +- fix(tests): resolve all phpunit failures — clean-DB suite green (#4626) (#4691) by @jekkos +- fix(licenses): guard malformed data, parallelize gulp tasks, require Node 20 by @objecttothis +- fix(validation): broaden sendmail path regex, expand i18n, strip advisory IDs by @objecttothis +- fix(security): handle special characters in `.env` key values and improve insertion logic (#4656) by @objecttothis +- fix(locale): validate language_code against known locales to block path traversal (#4704) by @jekkos +- Fix GHSA-frx7-c5vv-m3mr: recompute cashup total server-side and force owner identity (#4706) by @jekkos +- feat(security): add THROTTLE_KEY env-var fallback for throttle.key (#4707) by @jekkos +- fix(i18n): translate remaining English labels in Swiss German Items.php (#4701) by @Rayan Abdul Cader +- fix(ci): stop stamping app version onto master and branch Docker tags (#4709) by @jekkos +- chore(deps): bump fflate from 0.8.2 to 0.8.3 (#4690) by @dependabot[bot] +- fix(i18n): swap print_delay_autoreturn number/required messages in 5 locales (#4699) by @Rayan Abdul Cader +- chore(release): unified git-cliff release workflow (changelog + tag + optional bump) (#4711) by @jekkos +- fix(release): push changelog/bump to master via admin PAT (GITHUB_TOKEN blocked by branch protection) by @jekkos +- docs: add 3.4.2 changelog by @github-actions[bot] +- chore: bump version to 3.4.3 by @github-actions[bot] +- fix(security): HTML-escape attribute dropdown option labels in items attributes view (#4715) by @jekkos +- fix(release): keep package-lock.json version in sync on bump (#4718) by @jekkos +- fix(security): strip all HTML tags from $.notify alert messages (#4716) by @jekkos +- chore: strip advisory IDs from code comments and changelog (#4720) by @jekkos +- fix(security): report unwritable .env.lock, make throttle limits configurable (#4714) by @jekkos +- chore: reset 3.4.2 (undo premature 3.4.3 bump + stale changelog) for re-cut by @jekkos + ## [3.4.1] - 2025-06-05 - Feature: PSR-12 Compliant Indentation by @objecttothis in ([#4196](https://github.com/opensourcepos/opensourcepos/pull/4196)) - Add .env to dist zip by @jekkos in ([#4199](https://github.com/opensourcepos/opensourcepos/pull/4199)) diff --git a/INSTALL.md b/INSTALL.md index b688cf972..eb75cc034 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,7 +10,7 @@ ### Allowed Hostnames (REQUIRED for Production) -⚠️ **CRITICAL**: OpenSourcePOS validates the Host header to prevent Host Header Injection attacks (GHSA-jchf-7hr6-h4f3). **You MUST configure `app.allowedHostnames` for production deployments. If not configured, the application will fail to start.** +⚠️ **CRITICAL**: OpenSourcePOS validates the Host header to prevent Host Header Injection attacks. **You MUST configure `app.allowedHostnames` for production deployments. If not configured, the application will fail to start.** **Add to your `.env` file:** @@ -34,9 +34,8 @@ RuntimeException: Security: allowedHostnames is not configured. **Solution**: Add `app.allowedHostnames` to your `.env` file with your domain(s). **Why this matters:** -- Prevents Host Header Injection attacks (GHSA-jchf-7hr6-h4f3) +- Prevents Host Header Injection attacks - Ensures URLs are generated with the correct domain -- Security advisory: https://github.com/opensourcepos/opensourcepos/security/advisories/GHSA-jchf-7hr6-h4f3 - Fixes issue #4480: .env configuration now works via comma-separated values ### HTTPS Behind Proxy @@ -72,7 +71,15 @@ Docker runs natively on Mac and Linux. Windows requires WSL2 to be installed. Pl **Be aware that this setup is not suited for production usage! Change the default passwords in the compose file before exposing the containers publicly.** -Start the containers using the following command +First create a **regular `.env` file** in the project root (a missing one is not +auto-created as a file — see the compose `create_host_path: false` setting). Copy +the shipped example: + +``` + cp .env.example .env +``` + +Then start the containers: ``` docker-compose up diff --git a/app/Commands/EnvProvision.php b/app/Commands/EnvProvision.php new file mode 100644 index 000000000..987b1d552 --- /dev/null +++ b/app/Commands/EnvProvision.php @@ -0,0 +1,126 @@ +key ?? ''); + + if ($key !== '' && strlen($key) >= 64) { + CLI::write('encryption.key : CI4 key already present', 'green'); + CLI::newLine(); + + return; + } + + $converter = new CI3SecretConverter(); + + if ($key !== '' && strlen($key) < 64) { + // DB read, safe outside the .env lock. + $plain = $converter->decryptAll($key); + $hasData = $this->anyNonEmpty($plain); + + // Backup -> rotate -> re-encrypt -> verify -> persist under a single .env lock. + rotateEncryptionKeyTransaction($key, static function () use ($plain, $hasData, $converter): void { + $encrypted = $converter->encryptAll($plain); + + if (array_diff_assoc($plain, $converter->verifyAll($encrypted)) !== []) { + throw new RuntimeException('Failed to verify converted encryption data.'); + } + + if ($hasData) { + $converter->saveAll($encrypted); + } + }); + + CLI::write('encryption.key : rotated CI3 -> CI4 key', 'green'); + if ($hasData) { + CLI::write('legacy secrets : converted and verified to CI4 cipher', 'green'); + } + } else { + // Fresh key (no old key to decrypt): a single atomic write suffices; + // the transaction wrapper still serialises it against other workers. + rotateEncryptionKeyTransaction(null, static function (): void {}); + + CLI::write('encryption.key : new CI4 key generated', 'green'); + + if ($this->legacySecretsPresent()) { + CLI::write('legacy secrets : WARNING - stored CI3 secrets found but no CI3 key to decrypt them; they could not be recovered', 'yellow'); + } + } + + CLI::newLine(); + CLI::write('env:provision complete.', 'green'); + CLI::newLine(); + } + + private function anyNonEmpty(array $plain): bool + { + foreach ($plain as $value) { + if ((string) $value !== '') { + return true; + } + } + + return false; + } + + private function legacySecretsPresent(): bool + { + try { + $appConfig = model('Appconfig'); + } catch (Exception $e) { + return false; + } + + foreach (CI3SecretConverter::LEGACY_KEYS as $col) { + try { + if ($appConfig->get_value($col) !== '') { + return true; + } + } catch (Exception $e) { + return false; + } + } + + return false; + } +} diff --git a/app/Config/App.php b/app/Config/App.php index ae61e7933..de204b036 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -12,7 +12,7 @@ class App extends BaseConfig * * @var string */ - public string $application_version = '3.4.2'; + public string $application_version = '3.4.3'; /** * This is the commit hash for the version you are currently using. @@ -307,7 +307,7 @@ class App extends BaseConfig /** * Validates and returns a trusted hostname. * - * Security: Prevents Host Header Injection attacks (GHSA-jchf-7hr6-h4f3) + * Security: Prevents Host Header Injection attacks * by validating the HTTP_HOST against a whitelist of allowed hostnames. * * In production: Fails fast if allowedHostnames is not configured. diff --git a/app/Config/Encryption.php b/app/Config/Encryption.php index 9147e1031..310a5a174 100644 --- a/app/Config/Encryption.php +++ b/app/Config/Encryption.php @@ -112,8 +112,47 @@ class Encryption extends BaseConfig parent::__construct(); if ($this->key === '') { - $envKey = getenv('ENCRYPTION_KEY'); - $this->key = $envKey === false ? '' : $envKey; + // Fallback sources (notably the ENCRYPTION_KEY Docker var, which the + // parent never reads) were not decode-parsed, so run them through + // the same parser to keep hex2bin:/base64: keys consistent. + $this->key = self::parseKey(self::resolveKey( + (string) ($_SERVER['encryption.key'] ?? ''), + (string) ($_ENV['encryption.key'] ?? ''), + (string) getenv('encryption.key'), + (string) getenv('ENCRYPTION_KEY'), + )); } } + + /** + * Decode a key's `hex2bin:`/`base64:` prefix, mirroring + * BaseConfig::parseEncryptionKey(); kept static so it is unit-testable. + */ + public static function parseKey(string $key): string + { + if (str_starts_with($key, 'hex2bin:')) { + return (string) hex2bin(substr($key, 8)); + } + + if (str_starts_with($key, 'base64:')) { + return (string) base64_decode(substr($key, 7), true); + } + + return $key; + } + + /** + * Return the first non-empty source (highest precedence first). Cascading + * past empty strings (vs `??`) avoids a blank value shadowing the real key. + */ + public static function resolveKey(string ...$sources): string + { + foreach ($sources as $source) { + if ($source !== '') { + return $source; + } + } + + return ''; + } } diff --git a/app/Config/Events.php b/app/Config/Events.php index 3a34f1cd5..d4c5a8792 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -28,9 +28,6 @@ use App\Events\Method; Events::on('pre_system', static function (): void { if (ENVIRONMENT !== 'testing') { - helper('security'); - checkThrottleEncryption(); - $value = ini_get('zlib.output_compression'); if (filter_var($value, FILTER_VALIDATE_BOOLEAN) || (int) $value > 0) { throw FrameworkException::forEnabledZlibOutputCompression(); diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 15071e1c3..9d6fb65ef 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -77,9 +77,9 @@ class Filters extends BaseFilters public array $globals = [ 'before' => [ 'honeypot', + 'isLoggedIn' => ['except' => 'login|migrate'], 'csrf' => ['except' => 'login|migrate'], 'invalidchars', - 'isLoggedIn' => ['except' => 'login|migrate'], ], 'after' => [ 'toolbar', diff --git a/app/Config/SecurityEnv.php b/app/Config/SecurityEnv.php new file mode 100644 index 000000000..89c974e4b --- /dev/null +++ b/app/Config/SecurityEnv.php @@ -0,0 +1,37 @@ +attribute->getAttributeInfo($definition_id); + $info = $this->attribute->getAttributeInfo($definitionId); foreach (get_object_vars($info) as $property => $value) { $info->$property = $value; } - $data['definition_id'] = $definition_id; - $data['definition_values'] = $this->attribute->getDefinitionValues($definition_id); - $data['definition_group'] = $this->attribute->getDefinitionsByType(GROUP, $definition_id); + $data['definition_id'] = $definitionId; + $data['definition_values'] = $this->attribute->getDefinitionValues($definitionId); + $data['definition_group'] = $this->attribute->getDefinitionsByType(GROUP, $definitionId); $data['definition_group'][''] = lang('Common.none_selected_text'); $data['definition_info'] = $info; - $show_all = Attribute::SHOW_IN_ITEMS | Attribute::SHOW_IN_RECEIVINGS | Attribute::SHOW_IN_SALES; - $data['definition_flags'] = $this->get_attributes($show_all); - $selected_flags = $info->definition_flags === '' ? $show_all : $info->definition_flags; - $data['selected_definition_flags'] = $this->get_attributes($selected_flags); + $showAll = Attribute::SHOW_IN_ITEMS | Attribute::SHOW_IN_RECEIVINGS | Attribute::SHOW_IN_SALES | Attribute::SHOW_IN_SEARCH; + $data['definition_flags'] = $this->get_attributes($showAll); + $selectedFlags = $info->definition_flags === '' ? $showAll : $info->definition_flags; + $data['selected_definition_flags'] = $this->get_attributes($selectedFlags); return view('attributes/form', $data); } diff --git a/app/Controllers/Cashups.php b/app/Controllers/Cashups.php index 0e9b06f23..5ecc9c441 100644 --- a/app/Controllers/Cashups.php +++ b/app/Controllers/Cashups.php @@ -211,20 +211,29 @@ class Cashups extends Secure_Controller $close_date = $this->request->getPost('close_date'); $close_date_formatter = date_create_from_format($this->config['dateformat'] . ' ' . $this->config['timeformat'], $close_date); + $open_amount_cash = parse_decimals($this->request->getPost('open_amount_cash')); + $transfer_amount_cash = parse_decimals($this->request->getPost('transfer_amount_cash')); + $closed_amount_cash = parse_decimals($this->request->getPost('closed_amount_cash')); + $closed_amount_due = parse_decimals($this->request->getPost('closed_amount_due')); + $closed_amount_card = parse_decimals($this->request->getPost('closed_amount_card')); + $closed_amount_check = parse_decimals($this->request->getPost('closed_amount_check')); + + $logged_in_employee = $this->employee->get_logged_in_employee_info(); + $cash_up_data = [ 'open_date' => $open_date_formatter->format('Y-m-d H:i:s'), 'close_date' => $close_date_formatter->format('Y-m-d H:i:s'), - 'open_amount_cash' => parse_decimals($this->request->getPost('open_amount_cash')), - 'transfer_amount_cash' => parse_decimals($this->request->getPost('transfer_amount_cash')), - 'closed_amount_cash' => parse_decimals($this->request->getPost('closed_amount_cash')), - 'closed_amount_due' => parse_decimals($this->request->getPost('closed_amount_due')), - 'closed_amount_card' => parse_decimals($this->request->getPost('closed_amount_card')), - 'closed_amount_check' => parse_decimals($this->request->getPost('closed_amount_check')), - 'closed_amount_total' => parse_decimals($this->request->getPost('closed_amount_total')), + 'open_amount_cash' => $open_amount_cash, + 'transfer_amount_cash' => $transfer_amount_cash, + 'closed_amount_cash' => $closed_amount_cash, + 'closed_amount_due' => $closed_amount_due, + 'closed_amount_card' => $closed_amount_card, + 'closed_amount_check' => $closed_amount_check, + 'closed_amount_total' => $this->_calculate_total($open_amount_cash, $transfer_amount_cash, $closed_amount_due, $closed_amount_cash, $closed_amount_card, $closed_amount_check), 'note' => $this->request->getPost('note') != null, 'description' => $this->request->getPost('description', FILTER_SANITIZE_FULL_SPECIAL_CHARS), - 'open_employee_id' => $this->request->getPost('open_employee_id', FILTER_SANITIZE_NUMBER_INT), - 'close_employee_id' => $this->request->getPost('close_employee_id', FILTER_SANITIZE_NUMBER_INT), + 'open_employee_id' => $logged_in_employee->person_id, + 'close_employee_id' => $logged_in_employee->person_id, 'deleted' => $this->request->getPost('deleted') != null ]; diff --git a/app/Controllers/Config.php b/app/Controllers/Config.php index a63bcaf8f..420c92b98 100644 --- a/app/Controllers/Config.php +++ b/app/Controllers/Config.php @@ -158,14 +158,20 @@ class Config extends Secure_Controller $file = file_get_contents('license/npm-prod.LICENSES'); $array = json_decode($file, true); - foreach ($array as $dependency) { - $license[$i]['text'] .= "library: {$dependency['name']}\n"; - $license[$i]['text'] .= "authors: {$dependency['author']}\n"; - $license[$i]['text'] .= "website: {$dependency['homepage']}\n"; - $license[$i]['text'] .= "version: {$dependency['installedVersion']}\n"; - $license[$i]['text'] .= "license: {$dependency['licenseType']}\n"; + if (is_array($array)) { + foreach ($array as $dependency) { + if (!is_array($dependency) || count(array_intersect(['name', 'author', 'homepage', 'installedVersion', 'licenseType'], array_keys($dependency))) !== 5) { + continue; + } - $license[$i]['text'] .= "\n"; + $license[$i]['text'] .= "library: {$dependency['name']}\n"; + $license[$i]['text'] .= "authors: {$dependency['author']}\n"; + $license[$i]['text'] .= "website: {$dependency['homepage']}\n"; + $license[$i]['text'] .= "version: {$dependency['installedVersion']}\n"; + $license[$i]['text'] .= "license: {$dependency['licenseType']}\n"; + + $license[$i]['text'] .= "\n"; + } } $license[$i]['text'] = rtrim($license[$i]['text'], "\n"); } @@ -178,14 +184,20 @@ class Config extends Secure_Controller $file = file_get_contents('license/npm-dev.LICENSES'); $array = json_decode($file, true); - foreach ($array as $dependency) { - $license[$i]['text'] .= "library: {$dependency['name']}\n"; - $license[$i]['text'] .= "authors: {$dependency['author']}\n"; - $license[$i]['text'] .= "website: {$dependency['homepage']}\n"; - $license[$i]['text'] .= "version: {$dependency['installedVersion']}\n"; - $license[$i]['text'] .= "license: {$dependency['licenseType']}\n"; + if (is_array($array)) { + foreach ($array as $dependency) { + if (!is_array($dependency) || count(array_intersect(['name', 'author', 'homepage', 'installedVersion', 'licenseType'], array_keys($dependency))) !== 5) { + continue; + } - $license[$i]['text'] .= "\n"; + $license[$i]['text'] .= "library: {$dependency['name']}\n"; + $license[$i]['text'] .= "authors: {$dependency['author']}\n"; + $license[$i]['text'] .= "website: {$dependency['homepage']}\n"; + $license[$i]['text'] .= "version: {$dependency['installedVersion']}\n"; + $license[$i]['text'] .= "license: {$dependency['licenseType']}\n"; + + $license[$i]['text'] .= "\n"; + } } $license[$i]['text'] = rtrim($license[$i]['text'], "\n"); } @@ -254,7 +266,9 @@ class Config extends Secure_Controller $data['selected_image_allowed_types'] = explode(',', $this->config['image_allowed_types']); // Integrations Related fields - $data['mailchimp'] = []; + $data['mailchimp'] = []; + $data['smtp_pass_set'] = !empty($this->config['smtp_pass']); + $data['msg_pwd_set'] = !empty($this->config['msg_pwd']); if (checkEncryption()) { // TODO: Hungarian notation if (!isset($this->encrypter)) { @@ -262,9 +276,8 @@ class Config extends Secure_Controller $this->encrypter = Services::encrypter(); } - $data['mailchimp']['api_key'] = (isset($this->config['mailchimp_api_key']) && !empty($this->config['mailchimp_api_key'])) - ? $this->encrypter->decrypt($this->config['mailchimp_api_key']) - : ''; + $data['mailchimp']['api_key'] = ''; + $data['mailchimp']['api_key_set'] = !empty($this->config['mailchimp_api_key']); $data['mailchimp']['list_id'] = (isset($this->config['mailchimp_list_id']) && !empty($this->config['mailchimp_list_id'])) ? $this->encrypter->decrypt($this->config['mailchimp_list_id']) @@ -273,8 +286,9 @@ class Config extends Secure_Controller // Remove any backup of .env created by check_encryption() removeBackup(); } else { - $data['mailchimp']['api_key'] = ''; - $data['mailchimp']['list_id'] = ''; + $data['mailchimp']['api_key'] = ''; + $data['mailchimp']['api_key_set'] = false; + $data['mailchimp']['list_id'] = ''; } $data['mailchimp']['lists'] = $this->_mailchimp(); @@ -492,7 +506,12 @@ class Config extends Secure_Controller return $this->response->setJSON(['success' => false, 'message' => reset($errors)]); } - $exploded = explode(":", $this->request->getPost('language')); + $language = $this->request->getPost('language'); + if (!in_array($language, array_keys(get_languages()), true)) { + return $this->response->setJSON(['success' => false, 'message' => 'Invalid language']); + } + + $exploded = explode(":", $language); $currency_symbol = $this->request->getPost('currency_symbol'); $batch_save_data = [ 'currency_symbol' => htmlspecialchars($currency_symbol ?? ''), @@ -531,10 +550,12 @@ class Config extends Secure_Controller */ public function postSaveEmail(): ResponseInterface { - $password = ''; + $postedPass = (string) $this->request->getPost('smtp_pass'); - if (checkEncryption() && !empty($this->request->getPost('smtp_pass'))) { - $password = $this->encrypter->encrypt($this->request->getPost('smtp_pass')); + if (checkEncryption() && $postedPass !== '') { + $password = $this->encrypter->encrypt($postedPass); + } else { + $password = (string) ($this->config['smtp_pass'] ?? ''); } $protocol = $this->request->getPost('protocol'); @@ -582,10 +603,12 @@ class Config extends Secure_Controller */ public function postSaveMessage(): ResponseInterface { - $password = ''; + $postedPwd = (string) $this->request->getPost('msg_pwd'); - if (checkEncryption() && !empty($this->request->getPost('msg_pwd'))) { - $password = $this->encrypter->encrypt($this->request->getPost('msg_pwd')); + if (checkEncryption() && $postedPwd !== '') { + $password = $this->encrypter->encrypt($postedPwd); + } else { + $password = (string) ($this->config['msg_pwd'] ?? ''); } $batch_save_data = [ @@ -648,19 +671,25 @@ class Config extends Secure_Controller */ public function postSaveMailchimp(): ResponseInterface { - $api_key = ''; - $list_id = ''; + $postedKey = (string) $this->request->getPost('mailchimp_api_key'); + $postedList = (string) $this->request->getPost('mailchimp_list_id'); if (checkEncryption()) { - $api_key_unencrypted = $this->request->getPost('mailchimp_api_key'); - if (!empty($api_key_unencrypted)) { - $api_key = $this->encrypter->encrypt($api_key_unencrypted); - } + $api_key = $postedKey !== '' + ? $this->encrypter->encrypt($postedKey) + : (string) ($this->config['mailchimp_api_key'] ?? ''); - $list_id_unencrypted = $this->request->getPost('mailchimp_list_id'); - if (!empty($list_id_unencrypted)) { - $list_id = $this->encrypter->encrypt($list_id_unencrypted); - } + $list_id = $postedList !== '' + ? $this->encrypter->encrypt($postedList) + : (string) ($this->config['mailchimp_list_id'] ?? ''); + } else { + $api_key = $postedKey !== '' + ? (string) $postedKey + : (string) ($this->config['mailchimp_api_key'] ?? ''); + + $list_id = $postedList !== '' + ? (string) $postedList + : (string) ($this->config['mailchimp_list_id'] ?? ''); } $batch_save_data = ['mailchimp_api_key' => $api_key, 'mailchimp_list_id' => $list_id]; diff --git a/app/Controllers/Items.php b/app/Controllers/Items.php index f4a7b33bf..33529261b 100644 --- a/app/Controllers/Items.php +++ b/app/Controllers/Items.php @@ -76,7 +76,7 @@ class Items extends Secure_Controller { $this->session->set('allow_temp_items', 0); - $data['table_headers'] = get_items_manage_table_headers(); + $data['table_headers'] = getItemsManageTableHeaders(); // Restore stock_location from URL or session $stockLocation = $this->request->getGet('stock_location', FILTER_SANITIZE_NUMBER_INT); @@ -111,12 +111,14 @@ class Items extends Secure_Controller $search = $this->request->getGet('search', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $limit = $this->request->getGet('limit', FILTER_SANITIZE_NUMBER_INT); $offset = $this->request->getGet('offset', FILTER_SANITIZE_NUMBER_INT); - $sort = $this->sanitizeSortColumn(item_headers(), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'item_id'); $order = $this->request->getGet('order', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $this->item_lib->set_item_location($this->request->getGet('stock_location')); - $definitionNames = $this->attribute->getDefinitionsByFlags(Attribute::SHOW_IN_ITEMS); + $definitionNamesWithTypes = $this->attribute->getDefinitionsByFlags(Attribute::SHOW_IN_ITEMS, true); + $definitionIds = array_keys($definitionNamesWithTypes); + + $sort = $this->sanitizeSortColumn(itemSortColumns($definitionIds), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'items.item_id'); $filters = [ 'start_date' => $this->request->getGet('start_date'), @@ -129,25 +131,79 @@ class Items extends Secure_Controller 'search_custom' => false, 'is_deleted' => false, 'temporary' => false, - 'definition_ids' => array_keys($definitionNames) + 'definition_ids' => $definitionIds ]; // Check if any filter is set in the multiselect dropdown - $request_filters = array_fill_keys($this->request->getGet('filters', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? [], true); - $filters = array_merge($filters, $request_filters); - $items = $this->item->search($search, $filters, $limit, $offset, $sort, $order); - $total_rows = $this->item->get_found_rows($search, $filters); - $data_rows = []; + $requestFilters = array_fill_keys($this->request->getGet('filters', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? [], true); + $filters = array_merge($filters, $requestFilters); - foreach ($items->getResult() as $item) { - $data_rows[] = get_item_data_row($item); + // When search_custom is enabled, include attributes that are searchable but may not be visible in table + if (!empty($filters['search_custom'])) { + $searchableDefinitions = $this->attribute->getDefinitionsByFlags(Attribute::SHOW_IN_ITEMS | Attribute::SHOW_IN_SEARCH); + $filters['definition_ids'] = array_keys($searchableDefinitions); + } + + $items = $this->item->search($search, $filters, $limit, $offset, $sort, $order); + $itemResults = $items->getResult(); + $totalRows = $this->item->get_found_rows($search, $filters); + $taxPercentsByItemId = $this->buildTaxPercentsByItem($itemResults); + + $dataRows = []; + + foreach ($itemResults as $item) { + $dataRows[] = getItemDataRow($item, $definitionNamesWithTypes, $taxPercentsByItemId); if ($item->pic_filename !== null) { $this->update_pic_filename($item); } } - return $this->response->setJSON(['total' => $total_rows, 'rows' => $data_rows]); + return $this->response->setJSON(['total' => $totalRows, 'rows' => $dataRows]); + } + + /** + * @return array item_id => formatted tax percents string + */ + private function buildTaxPercentsByItem(array $items): array + { + $config = config(OSPOS::class)->settings; + $taxPercentsByItemId = []; + + if ($config['use_destination_based_tax']) { + $taxCategoryIds = array_unique(array_filter(array_map(fn ($item) => $item->tax_category_id, $items))); + + if (empty($taxCategoryIds)) { + return []; + } + + $taxCategoryNames = []; + foreach ($this->tax_category->get_multiple_info($taxCategoryIds)->getResult() as $taxCategoryInfo) { + $taxCategoryNames[$taxCategoryInfo->tax_category_id] = $taxCategoryInfo->tax_category; + } + + foreach ($items as $item) { + if ($item->tax_category_id !== null && isset($taxCategoryNames[$item->tax_category_id])) { + $taxPercentsByItemId[$item->item_id] = $taxCategoryNames[$item->tax_category_id]; + } + } + } else { + $itemIds = array_map(fn ($item) => $item->item_id, $items); + $itemTaxesByItemId = $this->item_taxes->getInfoMultiple($itemIds); + + foreach ($itemIds as $itemId) { + $taxPercents = ''; + foreach ($itemTaxesByItemId[$itemId] ?? [] as $taxInfo) { + $taxPercents .= to_tax_decimals($taxInfo['percent']) . '%, '; + } + + // Remove ', ' from last item + $taxPercents = substr($taxPercents, 0, -2); + $taxPercentsByItemId[$itemId] = !$taxPercents ? '-' : $taxPercents; + } + } + + return $taxPercentsByItemId; } /** @@ -276,17 +332,21 @@ class Items extends Secure_Controller } /** - * @param string $item_ids + * @param string $itemIds * @return ResponseInterface */ - public function getRow(string $item_ids): ResponseInterface // TODO: An array would be better for parameter. + public function getRow(string $itemIds): ResponseInterface // TODO: An array would be better for parameter. { - $item_infos = $this->item->get_multiple_info(explode(':', $item_ids), $this->item_lib->get_item_location()); + $itemInfos = $this->item->get_multiple_info(explode(':', $itemIds), $this->item_lib->get_item_location()); + $itemResults = $itemInfos->getResult(); + + $definitionNames = $this->attribute->getDefinitionsByFlags(Attribute::SHOW_IN_ITEMS, true); + $taxPercentsByItemId = $this->buildTaxPercentsByItem($itemResults); $result = []; - foreach ($item_infos->getResult() as $item_info) { - $result[$item_info->item_id] = get_item_data_row($item_info); + foreach ($itemResults as $itemInfo) { + $result[$itemInfo->item_id] = getItemDataRow($itemInfo, $definitionNames, $taxPercentsByItemId); } return $this->response->setJSON($result); diff --git a/app/Controllers/Secure_Controller.php b/app/Controllers/Secure_Controller.php index 36daeebd5..535825d3e 100644 --- a/app/Controllers/Secure_Controller.php +++ b/app/Controllers/Secure_Controller.php @@ -75,7 +75,17 @@ class Secure_Controller extends BaseController public function sanitizeSortColumn($headers, $field, $default): string { - return $field != null && in_array($field, array_keys(array_merge(...$headers))) ? $field : $default; + if ($field === null) { + return $default; + } + + // Flatten keys directly as array_merge() renumbers numeric string keys + $validColumns = []; + foreach ($headers as $header) { + $validColumns[] = (string) array_key_first($header); + } + + return in_array((string) $field, $validColumns, true) ? $field : $default; } /** diff --git a/app/Database/Migrations/20220127000000_convertToCI4.php b/app/Database/Migrations/20220127000000_convertToCI4.php index c600cdb4d..f860c7aaa 100644 --- a/app/Database/Migrations/20220127000000_convertToCI4.php +++ b/app/Database/Migrations/20220127000000_convertToCI4.php @@ -2,14 +2,12 @@ namespace App\Database\Migrations; -use App\Models\Appconfig; +use App\Libraries\CI3SecretConverter; use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge; use CodeIgniter\Database\Migration; use CodeIgniter\HTTP\Exceptions\RedirectException; -use Config\Encryption; -use Config\Services; -use ReflectionException; +use RuntimeException; class ConvertToCI4 extends Migration { @@ -33,18 +31,34 @@ class ConvertToCI4 extends Migration throw new DatabaseException('Migration script 3.4.0_CI4Conversion.sql failed. Check logs for details.'); } - $existingKey = config('Encryption')->key; + $existingKey = (string) config('Encryption')->key; - if (!empty($existingKey) && strlen($existingKey) < 64) { - $this->convertCI3EncryptedData(); - } else { - if (!checkEncryption()) { - abortEncryptionConversion(); - throw new DatabaseException('Failed to persist encryption key. Check logs for details.'); - } + // A valid CI4 key requires no write — just confirm it is usable. + if ($existingKey !== '' && strlen($existingKey) >= 64) { + checkEncryption(); + + return; } - removeBackup(); + // Every branch below writes to .env. If the runtime user cannot write + // it (e.g. Docker/Compose with a read-only .env mount), fail with an + // actionable message instead of a raw fopen() error deep in the writer. + if (!envFileIsWritable()) { + log_message('critical', 'Encryption key not provisioned and .env is not writable. Run `php spark env:provision` to generate one.'); + + throw new RuntimeException(lang('Error.encryption_key_not_provisioned')); + } + + if ($existingKey !== '' && strlen($existingKey) < 64) { + // Old CI3-era key: decrypt, rotate, re-encrypt, persist — all under + // a single .env lock (see convertCI3EncryptedData). + $this->convertCI3EncryptedData($existingKey); + } else { + // No key at all: provision a fresh one (single atomic write), then + // drop the incidental pre-write backup left behind by the rotation. + rotateEncryptionKey(null); + removeBackup(); + } } /** @@ -53,95 +67,35 @@ class ConvertToCI4 extends Migration public function down(): void {} /** - * @throws ReflectionException - */ - private function convertCI3EncryptedData(): void - { - $appConfig = model(Appconfig::class); - - $ci3EncryptedData = [ - 'clcdesq_api_key' => '', - 'clcdesq_api_url' => '', - 'mailchimp_api_key' => '', - 'mailchimp_list_id' => '', - 'smtp_pass' => '' - ]; - - foreach ($ci3EncryptedData as $key => $value) { - $ci3EncryptedData[$key] = $appConfig->get_value($key); - } - - $decryptedData = $this->decryptCI3Data($ci3EncryptedData); - - if (!checkEncryption()) { - abortEncryptionConversion(); - throw new DatabaseException('Failed to persist encryption key. Check logs for details.'); - } - - $ci4EncryptedData = $this->encryptData($decryptedData); - - $success = empty(array_diff_assoc($decryptedData, $this->decryptData($ci4EncryptedData))); - if (!$success) { - abortEncryptionConversion(); - throw new RedirectException('login'); // TODO: Need to figure out how to pass the error to the Login controller so that it gets displayed. - } - - if (!$appConfig->batch_save($ci4EncryptedData)) { - abortEncryptionConversion(); - throw new DatabaseException('Failed to save converted encryption data. Check logs for details.'); - } - } - - /** - * Decrypts CI3 encrypted data and returns the plaintext values. + * Decrypts legacy CI3-encrypted secrets with the old key, rotates to a + * fresh CI4 key, re-encrypts under the new key, verifies the round trip, + * and persists the result. * - * @param array $encryptedData Data encrypted using CI3 methodology. - * @return array Plaintext, unencrypted data. - */ - private function decryptCI3Data(array $encryptedData): array - { - $config = new Encryption(); - $config->driver = 'OpenSSL'; - $config->key = config('Encryption')->key; - $config->cipher = 'AES-128-CBC'; - $config->rawData = false; - $config->encryptKeyInfo = 'encryption'; - $config->authKeyInfo = 'authentication'; - - $encrypter = Services::encrypter($config); - - return array_map(function ($value) use ($encrypter) { - return !empty($value) ? $encrypter->decrypt($value) : ''; - }, $encryptedData); - } - - /** - * Encrypts data using CI4 algorithms. + * The actual decryption/encryption is delegated to CI3SecretConverter so + * the docker startup command (env:provision) shares the same code path. * - * @param array $plainData Data to be encrypted. - * @return array Encrypted data. + * @param string $oldKey the CI3-era key currently in .env + * @throws RedirectException */ - private function encryptData(array $plainData): array + private function convertCI3EncryptedData(string $oldKey): void { - $encrypter = Services::encrypter(); + $converter = new CI3SecretConverter(); - return array_map(function ($value) use ($encrypter) { - return $value !== '' ? $encrypter->encrypt($value) : ''; - }, $plainData); - } + // DB read, safe outside the .env lock. + $plain = $converter->decryptAll($oldKey); - /** - * Decrypts data using CI4 algorithms. - * - * @param array $encryptedData Data to be decrypted. - * @return array Decrypted data. - */ - private function decryptData(array $encryptedData): array - { - $encrypter = Services::encrypter(); + // Backup -> rotate -> re-encrypt -> verify -> persist under one .env + // lock. On any failure the transaction restores the pre-rotation .env + // (still holding the lock); on success it drops the backup. + rotateEncryptionKeyTransaction($oldKey, static function () use ($plain, $converter): void { + $encrypted = $converter->encryptAll($plain); - return array_map(function ($value) use ($encrypter) { - return !empty($value) ? $encrypter->decrypt($value) : ''; - }, $encryptedData); + // Verify the round trip before committing so we never lose data. + if (array_diff_assoc($plain, $converter->verifyAll($encrypted)) !== []) { + throw new RedirectException('login'); // TODO: Need to figure out how to pass the error to the Login controller so that it gets displayed. + } + + $converter->saveAll($encrypted); + }); } } diff --git a/app/Events/Load_config.php b/app/Events/Load_config.php index 89e4ece27..d98d68e66 100644 --- a/app/Events/Load_config.php +++ b/app/Events/Load_config.php @@ -62,6 +62,10 @@ class Load_config private function languageExists(string $languageCode): bool { + if (strpbrk($languageCode, '/\\') !== false || str_contains($languageCode, '..') || str_contains($languageCode, "\0")) { + return false; + } + return file_exists(APPPATH . 'Language/' . $languageCode); } } diff --git a/app/Filters/IsLoggedIn.php b/app/Filters/IsLoggedIn.php index f300ba363..98ab87069 100644 --- a/app/Filters/IsLoggedIn.php +++ b/app/Filters/IsLoggedIn.php @@ -15,8 +15,19 @@ class IsLoggedIn implements FilterInterface $employee = model(Employee::class); if (!$employee->is_logged_in()) { + if ($request->isAJAX()) { + return service('response') + ->setStatusCode(401) + ->setJSON([ + 'success' => false, + 'message' => 'Session expired. Please log in again.', + ]); + } + throw new RedirectException('login'); } + + return null; } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) diff --git a/app/Filters/Throttle.php b/app/Filters/Throttle.php index 75ca63a79..ca4a558ab 100644 --- a/app/Filters/Throttle.php +++ b/app/Filters/Throttle.php @@ -8,22 +8,37 @@ use CodeIgniter\HTTP\ResponseInterface; use Config\Services; /** - * Rate limits login/migrate POST attempts, keyed by IP and by submitted - * username, to mitigate brute-force and credential-stuffing attacks - * (GHSA-hm9c-xchj-xgcp). Backed by CodeIgniter's cache-based Throttler, - * so limits are per-server (not shared across nodes on file cache). + * Rate limits login/migrate POST attempts by IP and submitted username to + * mitigate brute-force/credential-stuffing. Backed by CodeIgniter's + * cache-based Throttler, so limits are per-server (not shared on file cache). + * + * Tunable via `throttle.capacity` (default 5; 0 disables) and + * `throttle.seconds` (window, default 60) in .env. */ class Throttle implements FilterInterface { - private const CAPACITY = 5; - private const SECONDS = 60; - public function before(RequestInterface $request, $arguments = null) { if ($request->getMethod() !== 'POST') { return null; } + // Non-positive integer = explicit disable; missing/non-numeric falls + // back to the default so a typo (e.g. "five") cannot bypass lockout. + $capacity = filter_var(env('throttle.capacity'), FILTER_VALIDATE_INT); + if ($capacity === false) { + $capacity = 5; + } + if ($capacity <= 0) { + return null; + } + + $seconds = filter_var(env('throttle.seconds'), FILTER_VALIDATE_INT); + if ($seconds === false) { + $seconds = 60; + } + $seconds = max(1, $seconds); + helper('security'); $throttler = Services::throttler(); @@ -34,8 +49,8 @@ class Throttle implements FilterInterface $username = is_scalar($rawUsername) ? strtolower((string) $rawUsername) : ''; $usernameKey = $username !== '' ? 'login-user-' . hash_hmac('sha256', $username, $secret) : null; - $ipOk = $throttler->check($ipKey, self::CAPACITY, self::SECONDS); - $usernameOk = $usernameKey === null || $throttler->check($usernameKey, self::CAPACITY, self::SECONDS); + $ipOk = $throttler->check($ipKey, $capacity, $seconds); + $usernameOk = $usernameKey === null || $throttler->check($usernameKey, $capacity, $seconds); if (!$ipOk || !$usernameOk) { log_message('warning', 'Login throttled for IP {ip} (username: {username})', [ diff --git a/app/Helpers/security_helper.php b/app/Helpers/security_helper.php index 4b95506c1..3c384ce75 100644 --- a/app/Helpers/security_helper.php +++ b/app/Helpers/security_helper.php @@ -1,5 +1,6 @@ lockPath; $handle = @fopen($lockPath, 'c+'); if ($handle === false) { - throw new RuntimeException("Unable to open $lockPath"); + $reason = error_get_last()['message'] ?? 'unknown error'; + log_message('critical', "Unable to open $lockPath: $reason"); + + throw new RuntimeException(lang('Error.unable_to_open_lock_file', ['filePath' => $lockPath, 'reason' => $reason])); } if (!flock($handle, LOCK_EX)) { fclose($handle); - throw new RuntimeException("Unable to lock $lockPath"); + $reason = error_get_last()['message'] ?? 'unknown error'; + log_message('critical', "Unable to lock $lockPath: $reason"); + + throw new RuntimeException(lang('Error.unable_to_lock_file', ['filePath' => $lockPath, 'reason' => $reason])); } return $handle; @@ -41,16 +48,13 @@ function unlockEnvFile($handle): void } /** - * Replaces or inserts a single `key='value'` line in .env + * Copies .env.example to .env (or creates a stub) if .env does not exist yet. * - * @param string $envKey - * @param string $value - * @return bool true on success, false if the write could not be completed + * @param string $configPath + * @return bool true if $configPath exists (already did, or was just created) */ -function writeEnvKey(string $envKey, string $value): bool +function initializeEnvFile(string $configPath): bool { - $configPath = ROOTPATH . '.env'; - if (!file_exists($configPath)) { $examplePath = ROOTPATH . '.env.example'; if (file_exists($examplePath)) { @@ -61,56 +65,70 @@ function writeEnvKey(string $envKey, string $value): bool @chmod($configPath, 0640); } - if (!file_exists($configPath)) { + return file_exists($configPath); +} + +/** + * Replaces or inserts a single `key='value'` line in .env + * + * @param string $envKey + * @param string $value + * @return bool true on success, false if the write could not be completed + */ +function writeEnvKey(string $envKey, string $value): bool +{ + $configPath = config('SecurityEnv')->envPath; + + if (!initializeEnvFile($configPath)) { return false; } $lock = lockEnvFile(); try { - $configFile = file_get_contents($configPath); + $configFile = @file_get_contents($configPath); if ($configFile === false) { return false; } - $configFile = applyEnvKeyReplacement($configFile, $envKey, $value); + $updated = applyEnvKeyReplacement($configFile, $envKey, $value); + if ($updated === null) { + return false; + } - return atomicWriteFile($configPath, $configFile); + return atomicWriteFile($configPath, $updated); } finally { unlockEnvFile($lock); } } -/** - * @param string $configFile - * @param string $envKey - * @param string $value - * @return string - */ -function applyEnvKeyReplacement(string $configFile, string $envKey, string $value): string +function applyEnvKeyReplacement(string $configFile, string $envKey, string $value): ?string { $pattern = '/^\s*' . preg_quote($envKey, '/') . '\s*=.*/m'; + $escapedValue = str_replace(['\\', '$'], ['\\\\', '\\$'], $value); if (preg_match($pattern, $configFile)) { - return preg_replace($pattern, "$envKey='$value'", $configFile, 1); + return preg_replace_callback($pattern, static fn () => "$envKey='$escapedValue'", $configFile, 1); } + // New keys insert right after encryption.key so it stays first for easy backup/rotation. if (preg_match('/^encryption\.key\s*=.*$/m', $configFile, $matches, PREG_OFFSET_CAPTURE)) { - $insertAt = $matches[0][1] + strlen($matches[0][0]); + $insertAt = (int) $matches[0][1] + strlen($matches[0][0]); - return substr_replace($configFile, "\n$envKey='$value'", $insertAt, 0); + return substr_replace($configFile, "\n$envKey='$escapedValue'", $insertAt, 0); } - return $configFile . "\n$envKey='$value'\n"; + return $configFile . "\n$envKey='$escapedValue'\n"; } /** * Writes $contents to a temp file in the same directory as $path, then - * renames it onto $path so readers never observe a partially-written file. + * renames it onto $path so readers never observe a partially written file. * * @param string $path * @param string $contents * @return bool + * @throws RandomException */ function atomicWriteFile(string $path, string $contents): bool { @@ -145,10 +163,7 @@ function atomicWriteFile(string $path, string $contents): bool fclose($handle); - // rename() overwrites an existing destination on POSIX. On Windows it - // does not, so fall back to unlink()+rename() there. Callers must not - // hold any open handle on $path — Windows can't unlink/rename a path - // that's still open, even by the same process. + // On Windows rename() does not overwrite an existing destination. Fall back to unlink()+rename(). if (!@rename($tmpPath, $path)) { if (PHP_OS_FAMILY !== 'Windows' || !@unlink($path) || !@rename($tmpPath, $path)) { @unlink($tmpPath); @@ -163,105 +178,390 @@ function atomicWriteFile(string $path, string $contents): bool } /** - * @return bool + * Copies $configPath to $backupPath, creating the backup folder if needed. + * + * @param string $configPath + * @param string $backupPath + * @return bool true when the backup exists and is readable, false otherwise */ -function checkEncryption(): bool +function backupEnvFile(string $configPath, string $backupPath): bool { - $oldKey = config('Encryption')->key; + $backupFolder = dirname($backupPath); - if ((empty($oldKey)) || (strlen($oldKey) < 64)) { - $encryption = new Encryption(); - $key = bin2hex($encryption->createKey()); - config('Encryption')->key = $key; + if (!file_exists($backupFolder) && !@mkdir($backupFolder, 0750, true)) { + return false; + } - $configPath = ROOTPATH . '.env'; - $backupPath = WRITEPATH . '/backup/.env.bak'; - $backupFolder = WRITEPATH . '/backup'; + if (!@copy($configPath, $backupPath)) { + return false; + } - if (!file_exists($backupFolder)) { - @mkdir($backupFolder, 0750, true); - } + if (!is_readable($backupPath)) { + return false; + } - if (!file_exists($configPath)) { - $examplePath = ROOTPATH . '.env.example'; - if (file_exists($examplePath)) { - @copy($examplePath, $configPath); - } else { - @file_put_contents($configPath, "# OSPOS Configuration\n\n"); - } - @chmod($configPath, 0640); - } - - if (file_exists($configPath)) { - @copy($configPath, $backupPath); - @chmod($backupPath, 0640); - @chmod($configPath, 0640); - - if (!writeEnvKey('encryption.key', $key)) { - return false; - } - - if (!empty($oldKey)) { - $configFile = file_get_contents($configPath); - $oldLine = "# encryption.key='$oldKey' REMOVE IF UNNEEDED\r\n"; - if (preg_match('/^encryption\.key\s*=/m', $configFile, $matches, PREG_OFFSET_CAPTURE)) { - $configFile = substr_replace($configFile, $oldLine, $matches[0][1], 0); - @file_put_contents($configPath, $configFile); - @chmod($configPath, 0640); - } - } - - log_message('info', "Updated encryption key in $configPath"); - } + if (@chmod($backupPath, 0640) !== true || @chmod($configPath, 0640) !== true) { + return false; } return true; } /** - * Returns a persistent secret for HMAC-hashing login-throttle cache keys. + * Applies the new encryption.key to $configFile, preserving $oldKey as a + * commented-out backup line immediately before it. * - * Deliberately independent of checkEncryption()/encryption.key: the throttle - * filter runs before the login-triggered CI3->CI4 migration, so provisioning - * this secret must never touch or rotate the encryption key. + * @param string $configFile + * @param string $key + * @param string $oldKey + * @return string|null updated file contents, or null if the replacement failed + */ +function writeNewEncryptionKey(string $configFile, string $key, string $oldKey): ?string +{ + $updated = applyEnvKeyReplacement($configFile, 'encryption.key', $key); + if ($updated === null) { + return null; + } + + if (!empty($oldKey)) { + $oldLine = "# encryption.key='$oldKey' REMOVE IF UNNEEDED\r\n"; + if (preg_match('/^encryption\.key\s*=/m', $updated, $matches, PREG_OFFSET_CAPTURE)) { + $updated = substr_replace($updated, $oldLine, $matches[0][1], 0); + } + } + + return $updated; +} + +/** + * Returns true when the current process can write to (or create) .env. * - * @return string - * @throws RandomException - * @throws RuntimeException if the key cannot be durably persisted + * The write path (temp file + lock, then rename) needs write permission on + * the DIRECTORY, not on .env itself — a bind-mounted .env can be writable + * while the dir (or a root-owned .env.lock) is not, so .env's own mode is not + * a reliable signal. + * + * @return bool + */ +function envFileIsWritable(): bool +{ + $configPath = config('SecurityEnv')->envPath; + $lockPath = config('SecurityEnv')->lockPath; + $dir = dirname($configPath); + + if (!is_writable($dir)) { + return false; + } + + // Windows-only: rename() can't replace a read-only destination, so an + // existing .env must itself be writable (POSIX rename() can, if the dir is). + if (PHP_OS_FAMILY === 'Windows' + && file_exists($configPath) + && !is_writable($configPath) + ) { + return false; + } + + // An existing mutex file (e.g. left by a prior root env:provision) must be writable. + if (file_exists($lockPath) && !is_writable($lockPath)) { + return false; + } + + return true; +} + +/** + * Ensures a usable CI4 encryption key is available. + * + * Behaviour: + * - Key present and >= 64 characters: returns true immediately (no I/O). + * - Key missing or CI3-era (< 64 chars) and .env IS writable: + * - Short key: decrypts legacy CI3 secrets, rotates to a fresh CI4 key, + * re-encrypts under the new key, verifies the round-trip, persists result. + * - Missing key: generates a fresh key and persists it. + * - Key missing or CI3-era (< 64 chars) and .env NOT writable: + * throws — the key was presumably already provisioned externally + * (e.g. `php spark env:provision` at container startup); the in-memory + * config simply hasn't been reloaded yet. + * + * @param CI3SecretConverter|null $converter injectable converter used to + * decrypt/re-encrypt legacy CI3 + * secrets in the short-key branch; + * defaults to the real converter + * (tests may pass a fake to avoid + * hitting the database) + * @return bool true when a valid key is available + * @throws Throwable when .env is not writable or the CI3 -> CI4 conversion fails + */ +function checkEncryption(?CI3SecretConverter $converter = null): bool +{ + $key = (string) config('Encryption')->key; + + if ($key !== '' && strlen($key) >= 64) { + return true; + } + + if (!envFileIsWritable()) { + log_message('critical', 'Encryption key not provisioned and .env is not writable. Run `php spark env:provision` to generate one.'); + + throw new RuntimeException(lang('Error.encryption_key_not_provisioned')); + } + + if ($key !== '') { + $converter = $converter ?? new CI3SecretConverter(); + + // Legacy plaintext is a DB read; safe outside the .env lock (the lock + // only guards the .env file write, which the rotation performs). + $plain = $converter->decryptAll($key); + + $nonEmpty = false; + foreach ($plain as $value) { + if ((string) $value !== '') { + $nonEmpty = true; + break; + } + } + + // Run the whole backup -> rotate -> re-encrypt -> verify -> persist unit + // under a single .env lock. On any failure, the pre-rotation backup is + // restored (also in-lock); on success the backup is removed (in-lock). + rotateEncryptionKeyTransaction($key, static function () use ($plain, $converter, $nonEmpty): void { + $encrypted = $converter->encryptAll($plain); + + if (array_diff_assoc($plain, $converter->verifyAll($encrypted)) !== []) { + throw new RuntimeException(lang('Error.unable_to_persist_encryption_key', ['filePath' => config('SecurityEnv')->envPath])); + } + + if ($nonEmpty) { + $converter->saveAll($encrypted); + } + }); + } else { + // Fresh key (no old key to decrypt): a single atomic write suffices. + rotateEncryptionKey(null); + } + + return true; +} + +/** + * Returns the persistent HMAC secret used to hash login-throttle cache keys. + * + * Behaviour: + * - Key already present (throttle.key or THROTTLE_KEY): returned immediately (no I/O). + * - Key missing and .env IS writable: generates a fresh key and persists it. + * - Key missing and .env NOT writable: + * throws — the key was presumably already provisioned externally + * (e.g. `php spark env:provision` at container startup). + * + * THROTTLE_KEY is a real environment-variable fallback (mirroring ENCRYPTION_KEY): + * it is consulted only when throttle.key is empty, so it can be supplied via + * Docker/Compose without writing a shared secret into .env. + * + * @return string the throttle key + * @throws RuntimeException if the key cannot be provisioned */ function checkThrottleEncryption(): string { $key = (string) env('throttle.key', ''); - if (!empty($key)) { + if ($key === '') { + $envKey = getenv('THROTTLE_KEY'); + $key = $envKey === false ? '' : $envKey; + } + + if ($key !== '') { return $key; } - $configPath = ROOTPATH . '.env'; + if (!envFileIsWritable()) { + log_message('critical', 'Throttle key not provisioned and .env is not writable. Run `php spark env:provision` to generate one.'); - if (!file_exists($configPath)) { - $examplePath = ROOTPATH . '.env.example'; - if (file_exists($examplePath)) { - @copy($examplePath, $configPath); - } else { - @file_put_contents($configPath, "# OSPOS Configuration\n\n"); - } - @chmod($configPath, 0640); + throw new RuntimeException(lang('Error.throttle_key_not_provisioned')); } - if (!file_exists($configPath)) { - throw new RuntimeException("Unable to create $configPath to provision throttle.key"); + return provisionThrottleKey(); +} + +/** + * Generates a strong encryption key, persists it to `.env` (rotating in place + * so callers can re-encrypt CI3 data that was sealed with the old key), and + * applies it to the running config instance. + * + * Standalone key-write path: acquires and holds the `.env` mutex for the + * duration of the write, so concurrent callers see atomic key writes. Callers + * that also run a longer multi-step conversion (re-encrypt, verify, persist) + * must use rotateEncryptionKeyTransaction() instead, so a single lock is held + * for the whole backup -> rotate -> re-encrypt -> persist unit and the + * key-write does not take a nested lock on the same mutex (which would + * deadlock). + * + * @param string|null $oldKey current key to rotate from; when non-empty it is + * preserved as a commented backup line for the + * CI3->CI4 re-encryption pass + * @return string the newly generated key + * @throws RuntimeException if the key could not be generated or persisted + * @throws RandomException + */ +function rotateEncryptionKey(?string $oldKey = null): string +{ + $lock = lockEnvFile(); + + try { + return rotateEncryptionKeyUnlock($oldKey); + } finally { + unlockEnvFile($lock); + } +} + +/** + * Lock-free core of rotateEncryptionKey(): generates a key, backs up .env, + * writes the new key (atomic), and applies it to the running config. + * + * Do not call directly from context that does not already hold the lock — + * that leaves a window in which another worker can read or write .env before + * the atomic write lands. The two callers, rotateEncryptionKey() and + * rotateEncryptionKeyTransaction(), each acquire the lock first and pass the + * lock-free core through. + * + * @param string|null $oldKey current key to rotate from + * @return string the newly generated key + * @throws RuntimeException if the key could not be generated or persisted + * @throws RandomException + */ +function rotateEncryptionKeyUnlock(?string $oldKey): string +{ + $encryption = new Encryption(); + $key = bin2hex($encryption->createKey()); + + $configPath = config('SecurityEnv')->envPath; + $backupPath = config('SecurityEnv')->backupPath; + + if (!initializeEnvFile($configPath)) { + throw new RuntimeException(lang('Error.unable_to_create_env_file', ['filePath' => $configPath])); + } + + if (file_exists($configPath)) { + if (!backupEnvFile($configPath, $backupPath)) { + log_message('critical', "Unable to back up $configPath to $backupPath before rotation; aborting."); + + throw new RuntimeException(lang('Error.unable_to_persist_encryption_key', ['filePath' => $configPath])); + } + } + + $configFile = @file_get_contents($configPath); + if ($configFile === false) { + log_message('critical', "Unable to read $configPath before rotation; aborting."); + + throw new RuntimeException(lang('Error.unable_to_read_env_file', ['filePath' => $configPath])); + } + + $updated = writeNewEncryptionKey($configFile, $key, (string) $oldKey); + if ($updated === null) { + throw new RuntimeException(lang('Error.unable_to_persist_encryption_key', ['filePath' => $configPath])); + } + + if (!atomicWriteFile($configPath, $updated)) { + throw new RuntimeException(lang('Error.unable_to_persist_encryption_key', ['filePath' => $configPath])); + } + + config('Encryption')->key = $key; + + log_message('info', "Rotated encryption key in $configPath"); + + return $key; +} + +/** + * Runs the CI3 -> CI4 key conversion as a single transaction on the `.env` + * mutex. + * + * The lock is acquired BEFORE the .env backup and released only AFTER + * $conversion has run, so the entire unit (backup -> rotate -> re-encrypt -> + * verify -> persist) is atomic with respect to any other worker that also + * takes the lock. The $conversion callback must throw on failure; the caller + * is expected to catch the exception and roll back via + * abortEncryptionConversion(). + * + * Calls the lock-free rotateEncryptionKeyUnlock() internally so the + * transaction lock is not re-acquired on the same mutex (which would deadlock) + * — the mutex is held across the backup, the key write, the re-encryption, + * verification, and persistence. + * + * @param string|null $oldKey current key to rotate from + * @param callable $conversion callback(): void, called after the new key + * has been persisted and applied to the running + * config; expected to perform re-encryption, + * verification, and persistence. Must throw on + * failure; the pre-rotation backup is restored + * before the exception propagates. + * @return string the newly generated key + * @throws Throwable from $conversion (the lock is released in `finally`) + * @throws RuntimeException + * @throws RandomException + */ +function rotateEncryptionKeyTransaction(?string $oldKey, callable $conversion): string +{ + // Hold one lock for the entire transaction (backup -> rotate -> re-encrypt + // -> verify -> persist -> cleanup). If we cannot acquire it, we fail + // rather than silently run without synchronisation — the invariant (no + // interleaved key + ciphertext writes) requires it. + $lock = lockEnvFile(); + + try { + // Lock-free inner write: the outer lock IS the transaction lock, so no + // nested re-acquire on the same mutex (which would deadlock). + $newKey = rotateEncryptionKeyUnlock($oldKey); + + // Multi-step conversion (re-encrypt -> verify -> persist) runs while + // still holding the lock, so a concurrent worker cannot interleave its + // key write between the rotation and this ciphertext save. + $conversion(); + + // Success: drop the pre-rotation backup, in-lock. + removeBackup(); + + return $newKey; + } catch (Throwable $e) { + // Failure (CI4 EncryptionException / ReflectionException from saveAll, + // or a failed round-trip verify, or the rotation itself failed): + // restore the pre-rotation .env while still holding the lock, then + // rethrow. catch runs before finally, so the restore is atomic with + // the held lock. + abortEncryptionConversion(); + + throw $e; + } finally { + unlockEnvFile($lock); + } +} + +/** + * Ensures a persistent throttle secret exists, generating and persisting one + * to `.env` if it is missing. Idempotent: safe to call on every startup. + * + * @return string the throttle key + * @throws RuntimeException if the key could not be created or persisted + */ +function provisionThrottleKey(): string +{ + $configPath = config('SecurityEnv')->envPath; + + if (!initializeEnvFile($configPath)) { + throw new RuntimeException(lang('Error.unable_to_create_env_file', ['filePath' => $configPath])); } $lock = lockEnvFile(); try { - $configFile = file_get_contents($configPath); + $configFile = @file_get_contents($configPath); if ($configFile === false) { - throw new RuntimeException("Unable to read $configPath to provision throttle.key"); + log_message('critical', "Unable to read $configPath while provisioning throttle key; aborting."); + + throw new RuntimeException(lang('Error.unable_to_read_env_file', ['filePath' => $configPath])); } - // Another process may have provisioned the key while we waited for the lock. + $key = ''; if (preg_match('/^\s*throttle\.key\s*=\s*[\'"]?([^\'"\r\n]*)/m', $configFile, $matches)) { $existing = trim($matches[1]); if ($existing !== '') { @@ -269,13 +569,13 @@ function checkThrottleEncryption(): string } } - if (empty($key)) { + if ($key === '') { $key = bin2hex(random_bytes(32)); - $configFile = applyEnvKeyReplacement($configFile, 'throttle.key', $key); + } - if (!atomicWriteFile($configPath, $configFile)) { - throw new RuntimeException("Unable to persist throttle.key to $configPath"); - } + $updated = applyEnvKeyReplacement($configFile, 'throttle.key', $key); + if ($updated === null || !atomicWriteFile($configPath, $updated)) { + throw new RuntimeException(lang('Error.unable_to_persist_throttle_key', ['filePath' => $configPath])); } } finally { unlockEnvFile($lock); @@ -285,7 +585,7 @@ function checkThrottleEncryption(): string $_ENV['throttle.key'] = $key; $_SERVER['throttle.key'] = $key; - log_message('info', 'Provisioned throttle key in ' . ROOTPATH . '.env'); + log_message('info', 'Provisioned throttle key in ' . config('SecurityEnv')->envPath); return $key; } @@ -295,16 +595,29 @@ function checkThrottleEncryption(): string */ function abortEncryptionConversion(): void { - $configPath = ROOTPATH . '.env'; - $backupPath = WRITEPATH . '/backup/.env.bak'; + $configPath = config('SecurityEnv')->envPath; + $backupPath = config('SecurityEnv')->backupPath; if (!file_exists($backupPath)) { return; } - @chmod($configPath, 0640); + // A backup exists, so the restore must succeed or fail loudly; a silent + // failure would leave .env holding the new key while the DB still holds the + // old ciphertext, making the data undecryptable after the next restart. + if (!is_file($backupPath) || !is_readable($backupPath)) { + throw new RuntimeException(lang('Error.unable_to_read_env_file', ['filePath' => $backupPath])); + } + $configFile = file_get_contents($backupPath); - @file_put_contents($configPath, $configFile); + if ($configFile === false) { + throw new RuntimeException(lang('Error.unable_to_read_env_file', ['filePath' => $backupPath])); + } + + if (!atomicWriteFile($configPath, $configFile)) { + throw new RuntimeException(lang('Error.unable_to_persist_encryption_key', ['filePath' => $configPath])); + } + log_message('info', "Restored $configPath from backup"); } @@ -313,7 +626,7 @@ function abortEncryptionConversion(): void */ function removeBackup(): void { - $backupPath = WRITEPATH . '/backup/.env.bak'; + $backupPath = config('SecurityEnv')->backupPath; if (!file_exists($backupPath)) { return; } diff --git a/app/Helpers/tabular_helper.php b/app/Helpers/tabular_helper.php index 129e6c921..2670929c0 100644 --- a/app/Helpers/tabular_helper.php +++ b/app/Helpers/tabular_helper.php @@ -2,8 +2,6 @@ use App\Models\Attribute; use App\Models\Employee; -use App\Models\Item_taxes; -use App\Models\Tax_category; use CodeIgniter\Database\ResultInterface; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\Session\Session; @@ -402,10 +400,33 @@ function item_headers(): array ]; } +/** + * Get all sortable column keys for items table, including dynamic attribute columns. + * + * @param array|null $definitionIds Attribute definition IDs to append as sortable columns. + * If null, resolved via a query against SHOW_IN_ITEMS. + * @return array Array of column headers in the format sanitizeSortColumn() expects + */ +function itemSortColumns(?array $definitionIds = null): array +{ + if ($definitionIds === null) { + $attribute = model(Attribute::class); + $definitionIds = array_keys($attribute->getDefinitionsByFlags($attribute::SHOW_IN_ITEMS)); + } + + $headers = item_headers(); + + foreach ($definitionIds as $definitionId) { + $headers[] = [(string) $definitionId => '']; + } + + return $headers; +} + /** * Get the header for the items tabular view */ -function get_items_manage_table_headers(): string +function getItemsManageTableHeaders(): string { $attribute = model(Attribute::class); $config = config(OSPOS::class)->settings; @@ -421,8 +442,8 @@ function get_items_manage_table_headers(): string $headers[] = ['item_pic' => lang('Items.image'), 'sortable' => false]; - foreach ($definitionsWithTypes as $definition_id => $definitionInfo) { - $headers[] = [$definition_id => $definitionInfo['name'], 'sortable' => false]; + foreach ($definitionsWithTypes as $definitionId => $definitionInfo) { + $headers[] = [$definitionId => $definitionInfo['name'], 'sortable' => true]; } $headers[] = ['inventory' => '', 'escape' => false]; @@ -433,32 +454,17 @@ function get_items_manage_table_headers(): string /** * Get the html data row for the item + * + * @param object $item + * @param array $definitionNames Attribute definitions with types, keyed by definition_id (see Attribute::getDefinitionsByFlags(..., true)) + * @param array $taxPercentsByItemId Pre-computed tax percent strings, keyed by item_id (see Items::buildTaxPercentsByItem()) + * @return array */ -function get_item_data_row(object $item): array +function getItemDataRow(object $item, array $definitionNames, array $taxPercentsByItemId): array { - $attribute = model(Attribute::class); - $item_taxes = model(Item_taxes::class); - $tax_category = model(Tax_category::class); $config = config(OSPOS::class)->settings; - if ($config['use_destination_based_tax']) { - if ($item->tax_category_id == null) { // TODO: === ? - $tax_percents = '-'; - } else { - $tax_category_info = $tax_category->get_info($item->tax_category_id); - $tax_percents = $tax_category_info->tax_category; - } - } else { - $item_tax_info = $item_taxes->get_info($item->item_id); - $tax_percents = ''; - foreach ($item_tax_info as $tax_info) { - $tax_percents .= to_tax_decimals($tax_info['percent']) . '%, '; - } - - // Remove ', ' from last item - $tax_percents = substr($tax_percents, 0, -2); - $tax_percents = !$tax_percents ? '-' : $tax_percents; - } + $taxPercents = $taxPercentsByItemId[$item->item_id] ?? '-'; $controller = get_controller(); @@ -471,8 +477,8 @@ function get_item_data_row(object $item): array : glob("./uploads/item_pics/$item->pic_filename"); if (sizeof($images) > 0) { - $image_path = ltrim($images[0], './'); - $image .= 'Image thumbnail'; + $imagePath = ltrim($images[0], './'); + $image .= 'Image thumbnail'; } } @@ -480,8 +486,6 @@ function get_item_data_row(object $item): array $item->name .= NAME_SEPARATOR . $item->pack_name; } - $definition_names = $attribute->getDefinitionsByFlags($attribute::SHOW_IN_ITEMS, true); - $columns = [ 'items.item_id' => $item->item_id, 'item_number' => $item->item_number, @@ -491,7 +495,7 @@ function get_item_data_row(object $item): array 'cost_price' => to_currency($item->cost_price), 'unit_price' => to_currency($item->unit_price), 'quantity' => to_quantity_decimals($item->quantity), - 'tax_percents' => !$tax_percents ? '-' : $tax_percents, + 'tax_percents' => !$taxPercents ? '-' : $taxPercents, 'item_pic' => $image ]; @@ -524,7 +528,7 @@ function get_item_data_row(object $item): array ) ]; - return $columns + expand_attribute_values($definition_names, (array) $item) + $icons; + return $columns + expand_attribute_values($definitionNames, (array) $item) + $icons; } function giftcard_headers(): array diff --git a/app/Language/ar-EG/Attributes.php b/app/Language/ar-EG/Attributes.php index b286580a2..c754d6009 100644 --- a/app/Language/ar-EG/Attributes.php +++ b/app/Language/ar-EG/Attributes.php @@ -1,34 +1,36 @@ "الميزات لا يمكن أن تحتوي على ':' أو'|'", - "confirm_delete" => "هل أنت متأكد من أنك تريد حذف الميزات المحددة ؟", - "confirm_restore" => "هل أنت متأكد من أنك تريد استعادة السمة (السمات) المحددة؟", - "definition_cannot_be_deleted" => "لا يمكن حذف السمات المحددة", - "definition_invalid_group" => "المجموعة المحددة غير موجودة أو غير صالحة.", - "definition_error_adding_updating" => "لا يمكن إضافة السمة {0} أو تحديثها. يرجى التحقق من سجل الخطأ.", - "definition_flags" => "رؤية الميزات", - "definition_group" => "المجموعة", - "definition_id" => "كود", - "definition_name" => "إضافة ميزة", - "definition_name_required" => "اسم الميزة هي خانة اجبارية", - "definition_one_or_multiple" => "ميزة/ميزات", - "definition_successful_adding" => "لقد تم إضافة صنف بنجاح", - "definition_successful_deleted" => "لقد تم حذف ميزة بنجاح", - "definition_successful_updating" => "تم تعديل الميزة بنجاح", - "definition_type" => "نوع الميزة", - "definition_type_required" => "نوع الميزة هي خانة إجبارية", - "definition_unit" => "وحدة قياس", - "definition_values" => "قيمة الميزة", - "new" => "اضافة ميزة جديده", - "no_attributes_to_display" => "لا يوجد اصناف للعرض", - "receipt_visibility" => "وصل", - "show_in_items" => "اظهار في الصنف", - "show_in_items_visibility" => "الصنف", - "show_in_receipt" => "اظهار على الوصل", - "show_in_receivings" => "اظهار في استلام البضائع", - "show_in_receivings_visibility" => "استلام البضائع", - "show_in_sales" => "اظهار خلال البيع", - "show_in_sales_visibility" => "البيع", - "update" => "تحديث الميزات", + 'attribute_value_invalid_chars' => 'الميزات لا يمكن أن تحتوي على \':\' أو\'|\'', + 'confirm_delete' => 'هل أنت متأكد من أنك تريد حذف الميزات المحددة ؟', + 'confirm_restore' => 'هل أنت متأكد من أنك تريد استعادة السمة (السمات) المحددة؟', + 'definition_cannot_be_deleted' => 'لا يمكن حذف السمات المحددة', + 'definition_invalid_group' => 'المجموعة المحددة غير موجودة أو غير صالحة.', + 'definition_error_adding_updating' => 'لا يمكن إضافة السمة {0} أو تحديثها. يرجى التحقق من سجل الخطأ.', + 'definition_flags' => 'رؤية الميزات', + 'definition_group' => 'المجموعة', + 'definition_id' => 'كود', + 'definition_name' => 'إضافة ميزة', + 'definition_name_required' => 'اسم الميزة هي خانة اجبارية', + 'definition_one_or_multiple' => 'ميزة/ميزات', + 'definition_successful_adding' => 'لقد تم إضافة صنف بنجاح', + 'definition_successful_deleted' => 'لقد تم حذف ميزة بنجاح', + 'definition_successful_updating' => 'تم تعديل الميزة بنجاح', + 'definition_type' => 'نوع الميزة', + 'definition_type_required' => 'نوع الميزة هي خانة إجبارية', + 'definition_unit' => 'وحدة قياس', + 'definition_values' => 'قيمة الميزة', + 'new' => 'اضافة ميزة جديده', + 'no_attributes_to_display' => 'لا يوجد اصناف للعرض', + 'receipt_visibility' => 'وصل', + 'show_in_items' => 'اظهار في الصنف', + 'show_in_items_visibility' => 'الصنف', + 'show_in_receipt' => 'اظهار على الوصل', + 'show_in_receivings' => 'اظهار في استلام البضائع', + 'show_in_receivings_visibility' => 'استلام البضائع', + 'show_in_sales' => 'اظهار خلال البيع', + 'show_in_sales_visibility' => 'البيع', + 'show_in_search' => 'اظهار في البحث', + 'show_in_search_visibility' => 'بحث', + 'update' => 'تحديث الميزات', ]; diff --git a/app/Language/ar-EG/Config.php b/app/Language/ar-EG/Config.php index 5182c4784..af96e9bf5 100644 --- a/app/Language/ar-EG/Config.php +++ b/app/Language/ar-EG/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'الرسائل النصية المحفوظة', 'msg_msg_placeholder' => 'إذا أردت إستخدام قالب للرسائل القصيرة احفظه هنا. عدا ذلك أترك هذا الحقل فارغ.', 'msg_pwd' => 'SMS-API كلمة السر لـ', - 'msg_pwd_required' => 'مطلوب SMS-API كلمة السر لـ', 'msg_src' => 'SMS-API كود المرسل لـ', 'msg_src_required' => 'مطلوب SMS-API كود المرسل لـ', 'msg_uid' => 'SMS-API اسم المستخدم لـ', @@ -285,7 +284,7 @@ return [ 'right' => 'يمين', 'sales_invoice_format' => 'شكل فاتورة البيع', 'sales_quote_format' => 'شكل فاتورة عرض الاسعار', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'مسار sendmail غير صالح. يُسمح فقط بالحروف والأرقام والشرطات والشرطات السفلية والشرطات المائلة والشرطات المائلة العكسية والنقطتين الرأسيتين والمسافات والنقاط.', 'saved_successfully' => 'تم حفظ التهيئة بنجاح.', 'saved_unsuccessfully' => 'لم يتم حفظ التهيئة بنجاح.', 'security_issue' => 'تحذير من ثغرة أمنية', diff --git a/app/Language/ar-EG/Error.php b/app/Language/ar-EG/Error.php index 9c63bccaa..b33d9d526 100644 --- a/app/Language/ar-EG/Error.php +++ b/app/Language/ar-EG/Error.php @@ -1,6 +1,14 @@ "ليس لديك صلاحيات للوصول لهذا القسم", - "unknown" => "غير معروف", + 'no_permission_module' => 'ليس لديك صلاحيات للوصول لهذا القسم', + 'unable_to_create_env_file' => 'تعذر إنشاء {filePath} لتوفير throttle.key', + 'unable_to_lock_file' => 'تعذر قفل {filePath}: {reason}', + 'unable_to_open_lock_file' => 'تعذر فتح {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'تعذر حفظ throttle.key في {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'تعذر قراءة {filePath} لتوفير throttle.key', + 'unknown' => 'غير معروف', ]; diff --git a/app/Language/ar-EG/Reports.php b/app/Language/ar-EG/Reports.php index 2e1d77356..a4580f647 100644 --- a/app/Language/ar-EG/Reports.php +++ b/app/Language/ar-EG/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "معدل الضريبة", "taxes" => "الضرائب", "taxes_summary_report" => "تقرير ملخص الضرائب", + "toggle_cost_and_profit" => "", "total" => "الإجمالى", "total_inventory_value" => "إجمالى قيمة المخزن", "total_low_sell_quantity" => "مجموع الكميات حسب الصنف الاولي", diff --git a/app/Language/ar-LB/Attributes.php b/app/Language/ar-LB/Attributes.php index b286580a2..c754d6009 100644 --- a/app/Language/ar-LB/Attributes.php +++ b/app/Language/ar-LB/Attributes.php @@ -1,34 +1,36 @@ "الميزات لا يمكن أن تحتوي على ':' أو'|'", - "confirm_delete" => "هل أنت متأكد من أنك تريد حذف الميزات المحددة ؟", - "confirm_restore" => "هل أنت متأكد من أنك تريد استعادة السمة (السمات) المحددة؟", - "definition_cannot_be_deleted" => "لا يمكن حذف السمات المحددة", - "definition_invalid_group" => "المجموعة المحددة غير موجودة أو غير صالحة.", - "definition_error_adding_updating" => "لا يمكن إضافة السمة {0} أو تحديثها. يرجى التحقق من سجل الخطأ.", - "definition_flags" => "رؤية الميزات", - "definition_group" => "المجموعة", - "definition_id" => "كود", - "definition_name" => "إضافة ميزة", - "definition_name_required" => "اسم الميزة هي خانة اجبارية", - "definition_one_or_multiple" => "ميزة/ميزات", - "definition_successful_adding" => "لقد تم إضافة صنف بنجاح", - "definition_successful_deleted" => "لقد تم حذف ميزة بنجاح", - "definition_successful_updating" => "تم تعديل الميزة بنجاح", - "definition_type" => "نوع الميزة", - "definition_type_required" => "نوع الميزة هي خانة إجبارية", - "definition_unit" => "وحدة قياس", - "definition_values" => "قيمة الميزة", - "new" => "اضافة ميزة جديده", - "no_attributes_to_display" => "لا يوجد اصناف للعرض", - "receipt_visibility" => "وصل", - "show_in_items" => "اظهار في الصنف", - "show_in_items_visibility" => "الصنف", - "show_in_receipt" => "اظهار على الوصل", - "show_in_receivings" => "اظهار في استلام البضائع", - "show_in_receivings_visibility" => "استلام البضائع", - "show_in_sales" => "اظهار خلال البيع", - "show_in_sales_visibility" => "البيع", - "update" => "تحديث الميزات", + 'attribute_value_invalid_chars' => 'الميزات لا يمكن أن تحتوي على \':\' أو\'|\'', + 'confirm_delete' => 'هل أنت متأكد من أنك تريد حذف الميزات المحددة ؟', + 'confirm_restore' => 'هل أنت متأكد من أنك تريد استعادة السمة (السمات) المحددة؟', + 'definition_cannot_be_deleted' => 'لا يمكن حذف السمات المحددة', + 'definition_invalid_group' => 'المجموعة المحددة غير موجودة أو غير صالحة.', + 'definition_error_adding_updating' => 'لا يمكن إضافة السمة {0} أو تحديثها. يرجى التحقق من سجل الخطأ.', + 'definition_flags' => 'رؤية الميزات', + 'definition_group' => 'المجموعة', + 'definition_id' => 'كود', + 'definition_name' => 'إضافة ميزة', + 'definition_name_required' => 'اسم الميزة هي خانة اجبارية', + 'definition_one_or_multiple' => 'ميزة/ميزات', + 'definition_successful_adding' => 'لقد تم إضافة صنف بنجاح', + 'definition_successful_deleted' => 'لقد تم حذف ميزة بنجاح', + 'definition_successful_updating' => 'تم تعديل الميزة بنجاح', + 'definition_type' => 'نوع الميزة', + 'definition_type_required' => 'نوع الميزة هي خانة إجبارية', + 'definition_unit' => 'وحدة قياس', + 'definition_values' => 'قيمة الميزة', + 'new' => 'اضافة ميزة جديده', + 'no_attributes_to_display' => 'لا يوجد اصناف للعرض', + 'receipt_visibility' => 'وصل', + 'show_in_items' => 'اظهار في الصنف', + 'show_in_items_visibility' => 'الصنف', + 'show_in_receipt' => 'اظهار على الوصل', + 'show_in_receivings' => 'اظهار في استلام البضائع', + 'show_in_receivings_visibility' => 'استلام البضائع', + 'show_in_sales' => 'اظهار خلال البيع', + 'show_in_sales_visibility' => 'البيع', + 'show_in_search' => 'اظهار في البحث', + 'show_in_search_visibility' => 'بحث', + 'update' => 'تحديث الميزات', ]; diff --git a/app/Language/ar-LB/Config.php b/app/Language/ar-LB/Config.php index 30faacd61..7a40b96bf 100644 --- a/app/Language/ar-LB/Config.php +++ b/app/Language/ar-LB/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'الرسائل النصية المحفوظة', 'msg_msg_placeholder' => 'إذا أردت إستخدام قالب للرسائل القصيرة احفظه هنا. عدا ذلك أترك هذا الحقل فارغ.', 'msg_pwd' => 'SMS-API كلمة السر لـ', - 'msg_pwd_required' => 'مطلوب SMS-API كلمة السر لـ', 'msg_src' => 'SMS-API كود المرسل لـ', 'msg_src_required' => 'مطلوب SMS-API كود المرسل لـ', 'msg_uid' => 'SMS-API اسم المستخدم لـ', @@ -285,7 +284,7 @@ return [ 'right' => 'يمين', 'sales_invoice_format' => 'شكل فاتورة البيع', 'sales_quote_format' => 'شكل فاتورة عرض الاسعار', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'مسار sendmail غير صالح. يُسمح فقط بالحروف والأرقام والشرطات والشرطات السفلية والشرطات المائلة والشرطات المائلة العكسية والنقطتين الرأسيتين والمسافات والنقاط.', 'saved_successfully' => 'تم حفظ التهيئة بنجاح.', 'saved_unsuccessfully' => 'لم يتم حفظ التهيئة بنجاح.', 'security_issue' => 'تحذير من ثغرة أمنية', diff --git a/app/Language/ar-LB/Error.php b/app/Language/ar-LB/Error.php index 9c63bccaa..16e700537 100644 --- a/app/Language/ar-LB/Error.php +++ b/app/Language/ar-LB/Error.php @@ -1,6 +1,14 @@ "ليس لديك صلاحيات للوصول لهذا القسم", - "unknown" => "غير معروف", + 'no_permission_module' => 'ليس لديك صلاحيات للوصول لهذا القسم', + 'unable_to_create_env_file' => 'تعذر إنشاء {filePath} لتوفير throttle.key', + 'unable_to_lock_file' => 'تعذر قفل {filePath}: {reason}', + 'unable_to_open_lock_file' => 'تعذر فتح {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'تعذر حفظ throttle.key في {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'تعذر قراءة {filePath} لتوفير throttle.key', + 'unknown' => 'غير معروف', ]; diff --git a/app/Language/ar-LB/Reports.php b/app/Language/ar-LB/Reports.php index 2e1d77356..a4580f647 100644 --- a/app/Language/ar-LB/Reports.php +++ b/app/Language/ar-LB/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "معدل الضريبة", "taxes" => "الضرائب", "taxes_summary_report" => "تقرير ملخص الضرائب", + "toggle_cost_and_profit" => "", "total" => "الإجمالى", "total_inventory_value" => "إجمالى قيمة المخزن", "total_low_sell_quantity" => "مجموع الكميات حسب الصنف الاولي", diff --git a/app/Language/az/Attributes.php b/app/Language/az/Attributes.php index bd28a0a28..9f8e336de 100644 --- a/app/Language/az/Attributes.php +++ b/app/Language/az/Attributes.php @@ -1,34 +1,36 @@ "Atribut dəyəri bu ':' və ya '|' olmaz", - "confirm_delete" => "Seçilmiş Atributları silmək istədiyinizdən əminsinizmi?", - "confirm_restore" => "Seçilmiş atributları bərpa etmək istədiyinizə əminsinizmi?", - "definition_cannot_be_deleted" => "Seçilmiş xüsusiyyətləri silmək olmadı", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "{0} -in atributları əlavə oluna və yenilənə bilmədi. Lütfən XƏTA loq faylını yoxlayın.", - "definition_flags" => "Atribut görünüşü", - "definition_group" => "Qrup", - "definition_id" => "Id", - "definition_name" => "Atribut əlavə et", - "definition_name_required" => "Atribut adı zəruri bir sahədir", - "definition_one_or_multiple" => "atribut (lar)", - "definition_successful_adding" => "Malı uğurla əlavə etdiniz", - "definition_successful_deleted" => "Siz uğurla sildiniz", - "definition_successful_updating" => "Atributunuzu müvəffəqiyyətlə yenilədiniz", - "definition_type" => "Atribut Tipi", - "definition_type_required" => "Atribut növü tələb olunan sahədir", - "definition_unit" => "Ölçü vahidi", - "definition_values" => "Atribut dəyərləri", - "new" => "Yeni atributlar", - "no_attributes_to_display" => "Göstərmək üçün heç bir element yoxdur", - "receipt_visibility" => "Qəbz", - "show_in_items" => "Malları göstər", - "show_in_items_visibility" => "Mallar", - "show_in_receipt" => "Qəbzi göstərin", - "show_in_receivings" => "Alacaqlarda göstərin", - "show_in_receivings_visibility" => "Alınanlar", - "show_in_sales" => "Satışda göstərin", - "show_in_sales_visibility" => "Satışlar", - "update" => "Atributları yenilə", + 'attribute_value_invalid_chars' => 'Atribut dəyəri bu \':\' və ya \'|\' olmaz', + 'confirm_delete' => 'Seçilmiş Atributları silmək istədiyinizdən əminsinizmi?', + 'confirm_restore' => 'Seçilmiş atributları bərpa etmək istədiyinizə əminsinizmi?', + 'definition_cannot_be_deleted' => 'Seçilmiş xüsusiyyətləri silmək olmadı', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => '{0} -in atributları əlavə oluna və yenilənə bilmədi. Lütfən XƏTA loq faylını yoxlayın.', + 'definition_flags' => 'Atribut görünüşü', + 'definition_group' => 'Qrup', + 'definition_id' => 'Id', + 'definition_name' => 'Atribut əlavə et', + 'definition_name_required' => 'Atribut adı zəruri bir sahədir', + 'definition_one_or_multiple' => 'atribut (lar)', + 'definition_successful_adding' => 'Malı uğurla əlavə etdiniz', + 'definition_successful_deleted' => 'Siz uğurla sildiniz', + 'definition_successful_updating' => 'Atributunuzu müvəffəqiyyətlə yenilədiniz', + 'definition_type' => 'Atribut Tipi', + 'definition_type_required' => 'Atribut növü tələb olunan sahədir', + 'definition_unit' => 'Ölçü vahidi', + 'definition_values' => 'Atribut dəyərləri', + 'new' => 'Yeni atributlar', + 'no_attributes_to_display' => 'Göstərmək üçün heç bir element yoxdur', + 'receipt_visibility' => 'Qəbz', + 'show_in_items' => 'Malları göstər', + 'show_in_items_visibility' => 'Mallar', + 'show_in_receipt' => 'Qəbzi göstərin', + 'show_in_receivings' => 'Alacaqlarda göstərin', + 'show_in_receivings_visibility' => 'Alınanlar', + 'show_in_sales' => 'Satışda göstərin', + 'show_in_sales_visibility' => 'Satışlar', + 'show_in_search' => 'Axtarışda göstər', + 'show_in_search_visibility' => 'Axtarış', + 'update' => 'Atributları yenilə', ]; diff --git a/app/Language/az/Config.php b/app/Language/az/Config.php index 0ef2f38c6..dcfc65a30 100644 --- a/app/Language/az/Config.php +++ b/app/Language/az/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saxlanılan Mətn Mesajı', 'msg_msg_placeholder' => 'SMS şablonunu istifadə etmək istəyirsinizsə, mesajınızı buraya qeyd edin, əks halda qutunu boş buraxın.', 'msg_pwd' => 'SMS-API Şifrəsi', - 'msg_pwd_required' => 'SMS-API şifrəsi tələb olunan bir sahədir', 'msg_src' => 'SMS-API Göndərici ID', 'msg_src_required' => 'SMS-API Göndərici ID tələb olunan sahədir', 'msg_uid' => 'SMS-API İstifadəçi adı', @@ -285,7 +284,7 @@ return [ 'right' => 'Sağ', 'sales_invoice_format' => 'Satış Fatura Formatı', 'sales_quote_format' => 'Satış Sitat Formati', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Yanlış sendmail yolu. Yalnız hərflərə, rəqəmlərə, tirelərə, alt xətlərə, əyri xətlərə, tərs əyri xətlərə, iki nöqtəyə, boşluqlara və nöqtələrə icazə verilir.', 'saved_successfully' => 'Konfiqurasiya uğurla saxlanıldı.', 'saved_unsuccessfully' => 'Konfiqurasiyanı saxlamq mümkün olmadı.', 'security_issue' => 'Təhlükəsizlik açığı xəbərdarlığı', diff --git a/app/Language/az/Error.php b/app/Language/az/Error.php index 383c3771a..32fd7658a 100644 --- a/app/Language/az/Error.php +++ b/app/Language/az/Error.php @@ -1,6 +1,14 @@ "sizin icazəniz yoxdur", - "unknown" => "naməlum", + 'no_permission_module' => 'sizin icazəniz yoxdur', + 'unable_to_create_env_file' => 'throttle.key təmin etmək üçün {filePath} yaradıla bilmədi', + 'unable_to_lock_file' => '{filePath} kilidlənə bilmədi: {reason}', + 'unable_to_open_lock_file' => '{filePath} açıla bilmədi: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key {filePath} faylına yazıla bilmədi', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'throttle.key təmin etmək üçün {filePath} oxuna bilmədi', + 'unknown' => 'naməlum', ]; diff --git a/app/Language/bg/Attributes.php b/app/Language/bg/Attributes.php index 3213b12e4..0342064f7 100644 --- a/app/Language/bg/Attributes.php +++ b/app/Language/bg/Attributes.php @@ -1,34 +1,36 @@ "", - "confirm_delete" => "", - "confirm_restore" => "", - "definition_cannot_be_deleted" => "", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "", - "definition_flags" => "", - "definition_group" => "", - "definition_id" => "", - "definition_name" => "", - "definition_name_required" => "", - "definition_one_or_multiple" => "", - "definition_successful_adding" => "", - "definition_successful_deleted" => "", - "definition_successful_updating" => "", - "definition_type" => "", - "definition_type_required" => "", - "definition_unit" => "", - "definition_values" => "", - "new" => "", - "no_attributes_to_display" => "", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => '', + 'confirm_delete' => '', + 'confirm_restore' => '', + 'definition_cannot_be_deleted' => '', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => '', + 'definition_flags' => '', + 'definition_group' => '', + 'definition_id' => '', + 'definition_name' => '', + 'definition_name_required' => '', + 'definition_one_or_multiple' => '', + 'definition_successful_adding' => '', + 'definition_successful_deleted' => '', + 'definition_successful_updating' => '', + 'definition_type' => '', + 'definition_type_required' => '', + 'definition_unit' => '', + 'definition_values' => '', + 'new' => '', + 'no_attributes_to_display' => '', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'Показване в търсенето', + 'show_in_search_visibility' => 'Търсене', + 'update' => '', ]; diff --git a/app/Language/bg/Config.php b/app/Language/bg/Config.php index c932fd673..9bf239650 100644 --- a/app/Language/bg/Config.php +++ b/app/Language/bg/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Невалиден път до sendmail. Разрешени са само букви, цифри, тирета, долни черти, наклонени черти, обратни наклонени черти, двоеточия, интервали и точки.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/bg/Error.php b/app/Language/bg/Error.php index 1c95b608d..bf53a7687 100644 --- a/app/Language/bg/Error.php +++ b/app/Language/bg/Error.php @@ -1,6 +1,14 @@ "Нямате разрешение за достъп до модула с име", - "unknown" => "Неизвестна грешка", + 'no_permission_module' => 'Нямате разрешение за достъп до модула с име', + 'unable_to_create_env_file' => 'Неуспешно създаване на {filePath} за осигуряване на throttle.key', + 'unable_to_lock_file' => 'Неуспешно заключване на {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Неуспешно отваряне на {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Неуспешно записване на throttle.key в {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Неуспешно четене на {filePath} за осигуряване на throttle.key', + 'unknown' => 'Неизвестна грешка', ]; diff --git a/app/Language/bg/Reports.php b/app/Language/bg/Reports.php index fa31c61b5..581bb197d 100644 --- a/app/Language/bg/Reports.php +++ b/app/Language/bg/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "Taxes", "taxes_summary_report" => "Taxes Summary Report", + "toggle_cost_and_profit" => "", "total" => "Total", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "", diff --git a/app/Language/bs/Attributes.php b/app/Language/bs/Attributes.php index 36a640f0b..662b6954c 100644 --- a/app/Language/bs/Attributes.php +++ b/app/Language/bs/Attributes.php @@ -1,34 +1,36 @@ "Vrijednost atributa ne može sadržavati ':' ili '|'", - "confirm_delete" => "Da li ste sigurni da želite da izbrišete izabrani atribut?", - "confirm_restore" => "Da li ste sigurni da želite vratiti izabrane atribute?", - "definition_cannot_be_deleted" => "Nije moguće izbrisati izabrane atribut", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "Atribut {0} nije moguće dodati ili ažurirati. Molimo provjerite dnevnik grešaka.", - "definition_flags" => "Vidljivost atributa", - "definition_group" => "Grupa", - "definition_id" => "Id", - "definition_name" => "Dodaj Atribut", - "definition_name_required" => "Ime atributa je obavezno polje", - "definition_one_or_multiple" => "atributi", - "definition_successful_adding" => "Uspješno ste dodali stavku", - "definition_successful_deleted" => "Uspješno ste izbrisali", - "definition_successful_updating" => "Uspješno ste ažurirali atribute", - "definition_type" => "Tip atributa", - "definition_type_required" => "Tip atributa je obavezno polje", - "definition_unit" => "Jedinice mjere", - "definition_values" => "Vrijednosti atributa", - "new" => "Novi atribut", - "no_attributes_to_display" => "Nema stavki za prikaz", - "receipt_visibility" => "Račun", - "show_in_items" => "Prikaži u stavkama", - "show_in_items_visibility" => "Stavka", - "show_in_receipt" => "Prikaži u računu", - "show_in_receivings" => "Prikaži na ulazima", - "show_in_receivings_visibility" => "Ulazi", - "show_in_sales" => "Prikaži u prodaji", - "show_in_sales_visibility" => "Prodaja", - "update" => "Ažuriraj atribut", + 'attribute_value_invalid_chars' => 'Vrijednost atributa ne može sadržavati \':\' ili \'|\'', + 'confirm_delete' => 'Da li ste sigurni da želite da izbrišete izabrani atribut?', + 'confirm_restore' => 'Da li ste sigurni da želite vratiti izabrane atribute?', + 'definition_cannot_be_deleted' => 'Nije moguće izbrisati izabrane atribut', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Atribut {0} nije moguće dodati ili ažurirati. Molimo provjerite dnevnik grešaka.', + 'definition_flags' => 'Vidljivost atributa', + 'definition_group' => 'Grupa', + 'definition_id' => 'Id', + 'definition_name' => 'Dodaj Atribut', + 'definition_name_required' => 'Ime atributa je obavezno polje', + 'definition_one_or_multiple' => 'atributi', + 'definition_successful_adding' => 'Uspješno ste dodali stavku', + 'definition_successful_deleted' => 'Uspješno ste izbrisali', + 'definition_successful_updating' => 'Uspješno ste ažurirali atribute', + 'definition_type' => 'Tip atributa', + 'definition_type_required' => 'Tip atributa je obavezno polje', + 'definition_unit' => 'Jedinice mjere', + 'definition_values' => 'Vrijednosti atributa', + 'new' => 'Novi atribut', + 'no_attributes_to_display' => 'Nema stavki za prikaz', + 'receipt_visibility' => 'Račun', + 'show_in_items' => 'Prikaži u stavkama', + 'show_in_items_visibility' => 'Stavka', + 'show_in_receipt' => 'Prikaži u računu', + 'show_in_receivings' => 'Prikaži na ulazima', + 'show_in_receivings_visibility' => 'Ulazi', + 'show_in_sales' => 'Prikaži u prodaji', + 'show_in_sales_visibility' => 'Prodaja', + 'show_in_search' => 'Prikaži u pretrazi', + 'show_in_search_visibility' => 'Pretraga', + 'update' => 'Ažuriraj atribut', ]; diff --git a/app/Language/bs/Config.php b/app/Language/bs/Config.php index f195ba96f..bc30c80aa 100644 --- a/app/Language/bs/Config.php +++ b/app/Language/bs/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Snimljena tekst poruka', 'msg_msg_placeholder' => 'Ako želite koristiti SMS šablon, snimite poruku ovdje. U suprotnom ostavite prazno polje.', 'msg_pwd' => 'SMS-API lozinke', - 'msg_pwd_required' => 'SMS-API lozinke je obavezno polje', 'msg_src' => 'SMS-API ID pošiljaoca', 'msg_src_required' => 'SMS-API Id pošiljaoca je obavezno polje', 'msg_uid' => 'SMS-API korisnika', @@ -285,7 +284,7 @@ return [ 'right' => 'Desno', 'sales_invoice_format' => 'Format fakture', 'sales_quote_format' => 'Format navedene prodaje', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Nevažeća sendmail putanja. Dozvoljena su samo slova, brojevi, crtice, donje crte, kose crte, obrnute kose crte, dvotačke, razmaci i tačke.', 'saved_successfully' => 'Konfiguracija je uspješno snimljena.', 'saved_unsuccessfully' => 'Konfiguracija nije uspješno snimljena.', 'security_issue' => 'Upozorenje o sigurnosnoj ranjivosti', diff --git a/app/Language/bs/Error.php b/app/Language/bs/Error.php index 7ed5adf4e..865a963bd 100644 --- a/app/Language/bs/Error.php +++ b/app/Language/bs/Error.php @@ -1,6 +1,14 @@ "Nemate dozvolu za pristup modulu", - "unknown" => "Neočekivana greška", + 'no_permission_module' => 'Nemate dozvolu za pristup modulu', + 'unable_to_create_env_file' => 'Nije moguće kreirati {filePath} za obezbjeđivanje throttle.key', + 'unable_to_lock_file' => 'Nije moguće zaključati {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Nije moguće otvoriti {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Nije moguće trajno sačuvati throttle.key u {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Nije moguće pročitati {filePath} za obezbjeđivanje throttle.key', + 'unknown' => 'Neočekivana greška', ]; diff --git a/app/Language/bs/Reports.php b/app/Language/bs/Reports.php index 77e34226c..ab5369986 100644 --- a/app/Language/bs/Reports.php +++ b/app/Language/bs/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Poreska stopa", "taxes" => "Porezi", "taxes_summary_report" => "Zbirni izvještaj poreza", + "toggle_cost_and_profit" => "", "total" => "Ukupno", "total_inventory_value" => "Ukupan iznos zalihe", "total_low_sell_quantity" => "Ukupno količina niskih prodaja", diff --git a/app/Language/ckb/Attributes.php b/app/Language/ckb/Attributes.php index 77f189a9c..d5ba8eaa9 100644 --- a/app/Language/ckb/Attributes.php +++ b/app/Language/ckb/Attributes.php @@ -1,34 +1,36 @@ "بەهای تایبەتمەندیی ناتوانێت '_' یان ' لەخۆبگرێت|'", - "confirm_delete" => "ئایا دڵنیای کە دەتەوێت تایبەتمەندییە هەڵبژێردراوەکە(کان) بسڕیتەوە؟", - "confirm_restore" => "ئایا دڵنیای کە دەتەوێت تایبەتمەندییە هەڵبژێردراوەکە(کان) بگەڕێنیتەوە؟", - "definition_cannot_be_deleted" => "نەتوانرا تایبەتمەندی هەڵبژێردراو بسڕدرێتەوە", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "تایبەتمەندی {0} نەتوانرا زیاد بکرێت یان نوێ بکرێتەوە. تکایە لیستی هەڵەکان بپشکنە.", - "definition_flags" => "توانای بینراویی تایبەتمەندی", - "definition_group" => "گروپ", - "definition_id" => "ناسنامە", - "definition_name" => "تایبەتمەندی زیاد بکە", - "definition_name_required" => "ناوی تایبەتمەندی خانەیەکی پێویستە", - "definition_one_or_multiple" => "تایبەتمەندی(ەکان)", - "definition_successful_adding" => "بەسەرکەوتوویی ئایتمەکەت زیادکرد", - "definition_successful_deleted" => "بەسەرکەوتوویی سڕیوتەتەوە", - "definition_successful_updating" => "بەسەرکەوتوویی تایبەتمەندیت نوێ کردووەتەوە", - "definition_type" => "جۆری تایبەتمەندی", - "definition_type_required" => "جۆری تایبەتمەندی خانەیەکی پێویستە", - "definition_unit" => "یەکەی پێوانەکردن", - "definition_values" => "بەهاکانی تایبەتمەندی", - "new" => "تایبەتمەندی نوێ", - "no_attributes_to_display" => "هیچ تایبەتمەندییەک نییە بۆ پیشاندان", - "receipt_visibility" => "ڕەچەتە", - "show_in_items" => "لەناو ئایتمەکان نیشانی بدە", - "show_in_items_visibility" => "ئایتمەکان", - "show_in_receipt" => "لە ڕەچەتەکەدا نیشانی بدە", - "show_in_receivings" => "لە بەدەستگەیشتووەکاندا نیشانی بدە", - "show_in_receivings_visibility" => "بەدەستگەیشتووکان", - "show_in_sales" => "لە فرۆشتندا نیشانی بدە", - "show_in_sales_visibility" => "فرۆشتن", - "update" => "تایبەتمەندی نوێ بکەرەوە", + 'attribute_value_invalid_chars' => 'بەهای تایبەتمەندیی ناتوانێت \'_\' یان \' لەخۆبگرێت|\'', + 'confirm_delete' => 'ئایا دڵنیای کە دەتەوێت تایبەتمەندییە هەڵبژێردراوەکە(کان) بسڕیتەوە؟', + 'confirm_restore' => 'ئایا دڵنیای کە دەتەوێت تایبەتمەندییە هەڵبژێردراوەکە(کان) بگەڕێنیتەوە؟', + 'definition_cannot_be_deleted' => 'نەتوانرا تایبەتمەندی هەڵبژێردراو بسڕدرێتەوە', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'تایبەتمەندی {0} نەتوانرا زیاد بکرێت یان نوێ بکرێتەوە. تکایە لیستی هەڵەکان بپشکنە.', + 'definition_flags' => 'توانای بینراویی تایبەتمەندی', + 'definition_group' => 'گروپ', + 'definition_id' => 'ناسنامە', + 'definition_name' => 'تایبەتمەندی زیاد بکە', + 'definition_name_required' => 'ناوی تایبەتمەندی خانەیەکی پێویستە', + 'definition_one_or_multiple' => 'تایبەتمەندی(ەکان)', + 'definition_successful_adding' => 'بەسەرکەوتوویی ئایتمەکەت زیادکرد', + 'definition_successful_deleted' => 'بەسەرکەوتوویی سڕیوتەتەوە', + 'definition_successful_updating' => 'بەسەرکەوتوویی تایبەتمەندیت نوێ کردووەتەوە', + 'definition_type' => 'جۆری تایبەتمەندی', + 'definition_type_required' => 'جۆری تایبەتمەندی خانەیەکی پێویستە', + 'definition_unit' => 'یەکەی پێوانەکردن', + 'definition_values' => 'بەهاکانی تایبەتمەندی', + 'new' => 'تایبەتمەندی نوێ', + 'no_attributes_to_display' => 'هیچ تایبەتمەندییەک نییە بۆ پیشاندان', + 'receipt_visibility' => 'ڕەچەتە', + 'show_in_items' => 'لەناو ئایتمەکان نیشانی بدە', + 'show_in_items_visibility' => 'ئایتمەکان', + 'show_in_receipt' => 'لە ڕەچەتەکەدا نیشانی بدە', + 'show_in_receivings' => 'لە بەدەستگەیشتووەکاندا نیشانی بدە', + 'show_in_receivings_visibility' => 'بەدەستگەیشتووکان', + 'show_in_sales' => 'لە فرۆشتندا نیشانی بدە', + 'show_in_sales_visibility' => 'فرۆشتن', + 'show_in_search' => 'لە گەڕاندا نیشانی بدە', + 'show_in_search_visibility' => 'گەڕان', + 'update' => 'تایبەتمەندی نوێ بکەرەوە', ]; diff --git a/app/Language/ckb/Config.php b/app/Language/ckb/Config.php index f2403a0ef..b58331d09 100644 --- a/app/Language/ckb/Config.php +++ b/app/Language/ckb/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'دەقی نامەی پاشەکەوتکراو', 'msg_msg_placeholder' => 'ئەگەر دەتەوێت تێمپڵەیتی کورتەنامە بەکاربهێنیت ئەوا نامەکەت لێرەدا پاشەکەوت بکە، ئەگەرنا خانەکە بە بەتاڵی بهێڵەرەوە.', 'msg_pwd' => 'وشەی نهێنی کورتەنامە-ئەی پی ئای', - 'msg_pwd_required' => 'خانەی وشەی نهێنی کورتەنامە-ئەی پی ئای پێویستە', 'msg_src' => 'ناسنامەی نێردەری کورتەنامە-ئەی پی ئای', 'msg_src_required' => 'خانەی ناسنامەی کورتەنامە-ئەی پی ئای پێویستە', 'msg_uid' => 'ناوی بەکارهێنەری کورتەنامە-ئەی پی ئای', @@ -285,6 +284,7 @@ return [ 'right' => 'ڕاست', 'sales_invoice_format' => 'فۆڕماتی فاکتورەی فرۆشتن', 'sales_quote_format' => 'فۆڕماتی دەرخستەی نرخەکانی فرۆشتن', + 'mailpath_invalid' => 'ڕێچکەی sendmail نادروستە. تەنها پیت، ژمارە، هێڵی بەستەرەوە، هێڵی ژێرەوە، سلاشی ڕاست، سلاشی چەپ، دوو خاڵ، بۆشایی و خاڵ ڕێگەپێدراون.', 'saved_successfully' => 'پاشەکەوتکردنی ڕێکخستن سەرکەوتوو بوو.', 'saved_unsuccessfully' => 'پاشەکەوتکردنی ڕێکخستن سەرکەوتوو نەبوو.', 'security_issue' => 'ئاگادارکردنەوەی لاوازی ئاسایش', diff --git a/app/Language/ckb/Error.php b/app/Language/ckb/Error.php index 5e5bf4ce5..dd1f9db1d 100644 --- a/app/Language/ckb/Error.php +++ b/app/Language/ckb/Error.php @@ -1,6 +1,14 @@ "تۆ ڕێگەپێدانت نییە بۆ دەستگەیشتن بەو مۆدیولەی کە ناوی لێنراوە", - "unknown" => "هەڵەیەکی چاوەڕواننەکراو", + 'no_permission_module' => 'تۆ ڕێگەپێدانت نییە بۆ دەستگەیشتن بەو مۆدیولەی کە ناوی لێنراوە', + 'unable_to_create_env_file' => 'نەتوانرا {filePath} دروست بکرێت بۆ دابینکردنی throttle.key', + 'unable_to_lock_file' => 'نەتوانرا {filePath} قوفڵ بکرێت: {reason}', + 'unable_to_open_lock_file' => 'نەتوانرا {filePath} کرایەوە: {reason}', + 'unable_to_persist_throttle_key' => 'نەتوانرا throttle.key لە {filePath} پاشەکەوت بکرێت', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'نەتوانرا {filePath} بخوێنرێتەوە بۆ دابینکردنی throttle.key', + 'unknown' => 'هەڵەیەکی چاوەڕواننەکراو', ]; diff --git a/app/Language/ckb/Reports.php b/app/Language/ckb/Reports.php index 9b76e1d11..5f11f141b 100644 --- a/app/Language/ckb/Reports.php +++ b/app/Language/ckb/Reports.php @@ -128,6 +128,7 @@ return [ 'tax_rate' => "ڕێژەی باج", 'taxes' => "باجەکان", 'taxes_summary_report' => "‎ڕاپۆرتی پوختەی باجەکان", + 'toggle_cost_and_profit' => "", 'total' => "گشتی", 'total_inventory_value' => "کۆی بەهای جەرد", 'total_low_sell_quantity' => "کۆی گشتی بڕی فرۆشتنی کەم", diff --git a/app/Language/cs/Attributes.php b/app/Language/cs/Attributes.php index 3213b12e4..8af0b2289 100644 --- a/app/Language/cs/Attributes.php +++ b/app/Language/cs/Attributes.php @@ -1,34 +1,36 @@ "", - "confirm_delete" => "", - "confirm_restore" => "", - "definition_cannot_be_deleted" => "", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "", - "definition_flags" => "", - "definition_group" => "", - "definition_id" => "", - "definition_name" => "", - "definition_name_required" => "", - "definition_one_or_multiple" => "", - "definition_successful_adding" => "", - "definition_successful_deleted" => "", - "definition_successful_updating" => "", - "definition_type" => "", - "definition_type_required" => "", - "definition_unit" => "", - "definition_values" => "", - "new" => "", - "no_attributes_to_display" => "", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => '', + 'confirm_delete' => '', + 'confirm_restore' => '', + 'definition_cannot_be_deleted' => '', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => '', + 'definition_flags' => '', + 'definition_group' => '', + 'definition_id' => '', + 'definition_name' => '', + 'definition_name_required' => '', + 'definition_one_or_multiple' => '', + 'definition_successful_adding' => '', + 'definition_successful_deleted' => '', + 'definition_successful_updating' => '', + 'definition_type' => '', + 'definition_type_required' => '', + 'definition_unit' => '', + 'definition_values' => '', + 'new' => '', + 'no_attributes_to_display' => '', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'Zobrazit ve vyhledávání', + 'show_in_search_visibility' => 'Vyhledávání', + 'update' => '', ]; diff --git a/app/Language/cs/Config.php b/app/Language/cs/Config.php index 623c03797..8f92eae0e 100644 --- a/app/Language/cs/Config.php +++ b/app/Language/cs/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Neplatná cesta k sendmailu. Povolena jsou pouze písmena, číslice, pomlčky, podtržítka, lomítka, zpětná lomítka, dvojtečky, mezery a tečky.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/cs/Error.php b/app/Language/cs/Error.php index 64ebe1e41..8693db278 100644 --- a/app/Language/cs/Error.php +++ b/app/Language/cs/Error.php @@ -1,6 +1,14 @@ "", - "unknown" => "", + 'no_permission_module' => 'Nemáte oprávnění přistupovat k modulu s názvem', + 'unable_to_create_env_file' => 'Nelze vytvořit {filePath} pro zajištění throttle.key', + 'unable_to_lock_file' => 'Nelze uzamknout {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Nelze otevřít {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Nelze trvale uložit throttle.key do {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Nelze přečíst {filePath} pro zajištění throttle.key', + 'unknown' => 'Neočekávaná chyba', ]; diff --git a/app/Language/cs/Reports.php b/app/Language/cs/Reports.php index 21f5c3157..3bc924d36 100644 --- a/app/Language/cs/Reports.php +++ b/app/Language/cs/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "Daně", "taxes_summary_report" => "Přehled podle DPH", + "toggle_cost_and_profit" => "", "total" => "Celkem", "total_inventory_value" => "Celková cena skladu", "total_low_sell_quantity" => "", diff --git a/app/Language/da/Attributes.php b/app/Language/da/Attributes.php index fd643f036..8944f7498 100644 --- a/app/Language/da/Attributes.php +++ b/app/Language/da/Attributes.php @@ -1,34 +1,36 @@ "Egenskabens værdi kan ikke indeholde ':' or '|'", - "confirm_delete" => "Er du sikker på, at du vil slette de valgte egenskaber?", - "confirm_restore" => "Er du sikker på, at du vil gendanne de valgte egenskaber?", - "definition_cannot_be_deleted" => "De valgte egenskaber kunne ikke slettes", - "definition_invalid_group" => "Den valgte gruppe findes ikke eller er ugyldig.", - "definition_error_adding_updating" => "Egenskab {0} Kunne ikke tilføjes eller opdateres. Tjek venligst fejlprotokollen.", - "definition_flags" => "Egenskabens Synlighed", - "definition_group" => "Gruppe", - "definition_id" => "Id", - "definition_name" => "Tilføj egenskab", - "definition_name_required" => "Egenskabens navn er et obligatorisk felt", - "definition_one_or_multiple" => "Egenskab(er)", - "definition_successful_adding" => "Du har tilføjet en genstand", - "definition_successful_deleted" => "Du har slettet en genstand", - "definition_successful_updating" => "Du har opdateret en egenskab", - "definition_type" => "Egenskabs-type", - "definition_type_required" => "Egenskabs-type er et obligatorisk felt", - "definition_unit" => "Måleenhed", - "definition_values" => "Egenskabens værdier", - "new" => "Ny egenskab", - "no_attributes_to_display" => "Ingen genstande at vise", - "receipt_visibility" => "Kvittering", - "show_in_items" => "Vis i genstande", - "show_in_items_visibility" => "Genstande", - "show_in_receipt" => "Vis i kvittering", - "show_in_receivings" => "Vis i modtagelser", - "show_in_receivings_visibility" => "Modtagelser", - "show_in_sales" => "Vis i salg", - "show_in_sales_visibility" => "Salg", - "update" => "Opdater egenskab", + 'attribute_value_invalid_chars' => 'Egenskabens værdi kan ikke indeholde \':\' or \'|\'', + 'confirm_delete' => 'Er du sikker på, at du vil slette de valgte egenskaber?', + 'confirm_restore' => 'Er du sikker på, at du vil gendanne de valgte egenskaber?', + 'definition_cannot_be_deleted' => 'De valgte egenskaber kunne ikke slettes', + 'definition_invalid_group' => 'Den valgte gruppe findes ikke eller er ugyldig.', + 'definition_error_adding_updating' => 'Egenskab {0} Kunne ikke tilføjes eller opdateres. Tjek venligst fejlprotokollen.', + 'definition_flags' => 'Egenskabens Synlighed', + 'definition_group' => 'Gruppe', + 'definition_id' => 'Id', + 'definition_name' => 'Tilføj egenskab', + 'definition_name_required' => 'Egenskabens navn er et obligatorisk felt', + 'definition_one_or_multiple' => 'Egenskab(er)', + 'definition_successful_adding' => 'Du har tilføjet en genstand', + 'definition_successful_deleted' => 'Du har slettet en genstand', + 'definition_successful_updating' => 'Du har opdateret en egenskab', + 'definition_type' => 'Egenskabs-type', + 'definition_type_required' => 'Egenskabs-type er et obligatorisk felt', + 'definition_unit' => 'Måleenhed', + 'definition_values' => 'Egenskabens værdier', + 'new' => 'Ny egenskab', + 'no_attributes_to_display' => 'Ingen genstande at vise', + 'receipt_visibility' => 'Kvittering', + 'show_in_items' => 'Vis i genstande', + 'show_in_items_visibility' => 'Genstande', + 'show_in_receipt' => 'Vis i kvittering', + 'show_in_receivings' => 'Vis i modtagelser', + 'show_in_receivings_visibility' => 'Modtagelser', + 'show_in_sales' => 'Vis i salg', + 'show_in_sales_visibility' => 'Salg', + 'show_in_search' => 'Vis i søgning', + 'show_in_search_visibility' => 'Søgning', + 'update' => 'Opdater egenskab', ]; diff --git a/app/Language/da/Config.php b/app/Language/da/Config.php index 7190f300e..fd7e90a0a 100644 --- a/app/Language/da/Config.php +++ b/app/Language/da/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -237,8 +236,8 @@ return [ 'print_bottom_margin_number' => 'Margin Bottom must be a number.', 'print_bottom_margin_required' => 'Margin Bottom is a required field.', 'print_delay_autoreturn' => 'Autoreturn to Sale delay', - 'print_delay_autoreturn_number' => 'Autoreturn to Sale delay is a required field.', - 'print_delay_autoreturn_required' => 'Autoreturn to Sale delay must be a number.', + 'print_delay_autoreturn_number' => '', + 'print_delay_autoreturn_required' => '', 'print_footer' => 'Print Browser Footer', 'print_header' => 'Print Browser Header', 'print_left_margin' => 'Margin Left', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Ugyldig sendmail-sti. Kun bogstaver, tal, bindestreger, understregninger, skråstreger, omvendte skråstreger, kolon, mellemrum og punktummer er tilladt.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/da/Error.php b/app/Language/da/Error.php index ace67fbb1..04ce0fc09 100644 --- a/app/Language/da/Error.php +++ b/app/Language/da/Error.php @@ -1,6 +1,14 @@ "You do not have permission to access the module named", - "unknown" => "Unexpected error", + 'no_permission_module' => 'Du har ikke tilladelse til at tilgå det navngivne modul', + 'unable_to_create_env_file' => 'Kunne ikke oprette {filePath} for at klargøre throttle.key', + 'unable_to_lock_file' => 'Kunne ikke låse {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Kunne ikke åbne {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Kunne ikke gemme throttle.key i {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Kunne ikke læse {filePath} for at klargøre throttle.key', + 'unknown' => 'Uventet fejl', ]; diff --git a/app/Language/da/Reports.php b/app/Language/da/Reports.php index c45e50b78..9cf1665e2 100644 --- a/app/Language/da/Reports.php +++ b/app/Language/da/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/de-CH/Attributes.php b/app/Language/de-CH/Attributes.php index d76085ec7..f25b7bf63 100644 --- a/app/Language/de-CH/Attributes.php +++ b/app/Language/de-CH/Attributes.php @@ -1,34 +1,36 @@ "", - "confirm_delete" => "", - "confirm_restore" => "", - "definition_cannot_be_deleted" => "", - "definition_invalid_group" => "Die ausgewählte Gruppe existiert nicht oder ist ungültig.", - "definition_error_adding_updating" => "", - "definition_flags" => "", - "definition_group" => "", - "definition_id" => "", - "definition_name" => "", - "definition_name_required" => "", - "definition_one_or_multiple" => "", - "definition_successful_adding" => "", - "definition_successful_deleted" => "", - "definition_successful_updating" => "", - "definition_type" => "", - "definition_type_required" => "", - "definition_unit" => "", - "definition_values" => "", - "new" => "", - "no_attributes_to_display" => "", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => '', + 'confirm_delete' => '', + 'confirm_restore' => '', + 'definition_cannot_be_deleted' => '', + 'definition_invalid_group' => 'Die ausgewählte Gruppe existiert nicht oder ist ungültig.', + 'definition_error_adding_updating' => '', + 'definition_flags' => '', + 'definition_group' => '', + 'definition_id' => '', + 'definition_name' => '', + 'definition_name_required' => '', + 'definition_one_or_multiple' => '', + 'definition_successful_adding' => '', + 'definition_successful_deleted' => '', + 'definition_successful_updating' => '', + 'definition_type' => '', + 'definition_type_required' => '', + 'definition_unit' => '', + 'definition_values' => '', + 'new' => '', + 'no_attributes_to_display' => '', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'In Suche anzeigen', + 'show_in_search_visibility' => 'Suche', + 'update' => '', ]; diff --git a/app/Language/de-CH/Config.php b/app/Language/de-CH/Config.php index a25d2f2c2..f02abe48e 100644 --- a/app/Language/de-CH/Config.php +++ b/app/Language/de-CH/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here. Otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Format Verkaufsrechnung', 'sales_quote_format' => '', - 'mailpath_invalid' => 'Ungültiger Sendmail-Pfad. Nur Buchstaben, Zahlen, Bindestriche, Unterstriche, Schrägstriche und Punkte sind erlaubt.', + 'mailpath_invalid' => 'Ungültiger Sendmail-Pfad. Nur Buchstaben, Zahlen, Bindestriche, Unterstriche, Schrägstriche, Rückwärtsschrägstriche, Doppelpunkte, Leerzeichen und Punkte sind erlaubt.', 'saved_successfully' => 'Einstellungen erfolgreich gesichert', 'saved_unsuccessfully' => 'Einstellungen konnten nicht gesichert werden', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/de-CH/Error.php b/app/Language/de-CH/Error.php index 23b161b63..60b048711 100644 --- a/app/Language/de-CH/Error.php +++ b/app/Language/de-CH/Error.php @@ -1,6 +1,14 @@ "Sie haben nicht die Zugangsrechte für das gewählte Modul", - "unknown" => "Unbekannter Fehler", + 'no_permission_module' => 'Sie haben nicht die Zugangsrechte für das gewählte Modul', + 'unable_to_create_env_file' => '{filePath} konnte nicht erstellt werden, um throttle.key bereitzustellen', + 'unable_to_lock_file' => '{filePath} konnte nicht gesperrt werden: {reason}', + 'unable_to_open_lock_file' => '{filePath} konnte nicht geöffnet werden: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key konnte nicht in {filePath} gespeichert werden', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => '{filePath} konnte nicht gelesen werden, um throttle.key bereitzustellen', + 'unknown' => 'Unbekannter Fehler', ]; diff --git a/app/Language/de-CH/Items.php b/app/Language/de-CH/Items.php index 7ea13990c..00ba32569 100644 --- a/app/Language/de-CH/Items.php +++ b/app/Language/de-CH/Items.php @@ -16,9 +16,9 @@ return [ 'change_all_to_not_allow_allow_desc' => 'Ändere alle zu NICHT Erlaube Alt. Bez.', 'change_all_to_serialized' => 'Ändere alle zu serialisiert', 'change_all_to_unserialized' => 'Ändere alle zu nicht serialisiert', - 'change_image' => 'Change Image', + 'change_image' => 'Bild ändern', 'confirm_bulk_edit' => 'Wollen Sie alle gewählten Artikel ändern?', - 'confirm_bulk_edit_wipe_taxes' => 'All item tax information will be replaced!', + 'confirm_bulk_edit_wipe_taxes' => 'Alle Artikel-Steuerinformationen werden ersetzt.', 'confirm_delete' => 'Wollen Sie alle gewählten Artikel löschen?', 'confirm_restore' => '', 'cost_price' => 'Einstandspreis', @@ -27,9 +27,9 @@ return [ 'count' => 'Ändere Bestand', 'csv_import_failed' => 'CSV Import fehlerhaft', 'csv_import_invalid_location' => '', - 'csv_import_nodata_wrongformat' => 'Your uploaded file has no data or wrong format', - 'csv_import_partially_failed' => 'Most Items imported. But some were not, here is the list', - 'csv_import_success' => 'Import of Items successful', + 'csv_import_nodata_wrongformat' => 'Die hochgeladene Datei enthält keine Daten oder ist falsch formatiert.', + 'csv_import_partially_failed' => 'Bei {0} Artikelimporten sind in den Zeilen {1} Fehler aufgetreten. Es wurden keine Zeilen importiert.', + 'csv_import_success' => 'Artikelimport erfolgreich.', 'current_quantity' => 'Aktuelle Menge', 'default_pack_name' => '', 'description' => 'Bezeichnung', @@ -45,7 +45,7 @@ return [ 'hsn_code' => '', 'image' => 'Bild', 'import_items_csv' => 'Importiere Artikel mit CSV Datei', - 'info_provided_by' => 'Info provided by', + 'info_provided_by' => 'Informationen bereitgestellt von', 'inventory' => 'Lagerbestand', 'inventory_CSV_import_quantity' => '', 'inventory_comments' => 'Bemerkungen', @@ -85,7 +85,7 @@ return [ 'quantity_number' => 'Menge muss eine Zahl sein', 'quantity_required' => 'Menge ist erforderlich', 'receiving_quantity' => 'Eingangsmenge', - 'remove_image' => 'Remove Image', + 'remove_image' => 'Bild löschen', 'reorder_level' => 'Mindestbestand', 'reorder_level_number' => 'Mindestbestand muss eine Zahl sein', 'reorder_level_required' => 'Mindestbestand ist erforderlich', @@ -93,7 +93,7 @@ return [ 'sales_tax_1' => 'Umsatzsteuer 1', 'sales_tax_2' => 'Umsatzsteuer 2', 'search_attributes' => 'Suche in Zusatzfeldern', - 'select_image' => 'Select Image', + 'select_image' => 'Bild auswählen', 'serialized_items' => 'Serialisierte Artikel', 'standard' => '', 'stock' => '', diff --git a/app/Language/de-CH/Reports.php b/app/Language/de-CH/Reports.php index cb31a533e..ef4717c2a 100644 --- a/app/Language/de-CH/Reports.php +++ b/app/Language/de-CH/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "Steuern", "taxes_summary_report" => "Bericht: Steuern (summarisch)", + "toggle_cost_and_profit" => "Kosten & Gewinn umschalten", "total" => "Total", "total_inventory_value" => "Total Inventarwert", "total_low_sell_quantity" => "", diff --git a/app/Language/de-DE/Attributes.php b/app/Language/de-DE/Attributes.php index cf7bd5348..4afd86482 100644 --- a/app/Language/de-DE/Attributes.php +++ b/app/Language/de-DE/Attributes.php @@ -1,34 +1,36 @@ "Attributwert darf nicht ':' oder '|' enthalten", - "confirm_delete" => "Sind Sie sicher, dass Sie die ausgewählten Attribute löschen möchten?", - "confirm_restore" => "Sind Sie sicher, dass Sie die ausgewählten Attribute wiederherstellen möchten?", - "definition_cannot_be_deleted" => "Ausgewählte Attribute konnten nicht gelöscht werden", - "definition_invalid_group" => "Die ausgewählte Gruppe existiert nicht oder ist ungültig.", - "definition_error_adding_updating" => "Das Attribut {0} konnte nicht hinzugefügt oder aktualisiert werden. Bitte überprüfen Sie den Error-Log.", - "definition_flags" => "Attribut Sichtbarkeit", - "definition_group" => "Gruppe", - "definition_id" => "ID", - "definition_name" => "Attribut hinzufügen", - "definition_name_required" => "Attributname ist ein Pflichtfeld", - "definition_one_or_multiple" => "Attribut(e)", - "definition_successful_adding" => "Artikel erfolgreich hinzugefügt", - "definition_successful_deleted" => "Löschung erfolgreich", - "definition_successful_updating" => "Sie haben das Attributerfolgreich aktualisiert", - "definition_type" => "Attribut Typ", - "definition_type_required" => "Attribut ist ein Pflichtfeld", - "definition_unit" => "Maßeinheit", - "definition_values" => "Attribut Werte", - "new" => "Neues Attribut", - "no_attributes_to_display" => "Keine Elemente zum Anzeigen", - "receipt_visibility" => "Quittung", - "show_in_items" => "In Artikeln anzeigen", - "show_in_items_visibility" => "Artikel", - "show_in_receipt" => "In Quittung anzeigen", - "show_in_receivings" => "In Eingängen anzeigen", - "show_in_receivings_visibility" => "Eingänge", - "show_in_sales" => "In Verkäufen anzeigen", - "show_in_sales_visibility" => "Verkauf", - "update" => "Attribut aktualisieren", + 'attribute_value_invalid_chars' => 'Attributwert darf nicht \':\' oder \'|\' enthalten', + 'confirm_delete' => 'Sind Sie sicher, dass Sie die ausgewählten Attribute löschen möchten?', + 'confirm_restore' => 'Sind Sie sicher, dass Sie die ausgewählten Attribute wiederherstellen möchten?', + 'definition_cannot_be_deleted' => 'Ausgewählte Attribute konnten nicht gelöscht werden', + 'definition_invalid_group' => 'Die ausgewählte Gruppe existiert nicht oder ist ungültig.', + 'definition_error_adding_updating' => 'Das Attribut {0} konnte nicht hinzugefügt oder aktualisiert werden. Bitte überprüfen Sie den Error-Log.', + 'definition_flags' => 'Attribut Sichtbarkeit', + 'definition_group' => 'Gruppe', + 'definition_id' => 'ID', + 'definition_name' => 'Attribut hinzufügen', + 'definition_name_required' => 'Attributname ist ein Pflichtfeld', + 'definition_one_or_multiple' => 'Attribut(e)', + 'definition_successful_adding' => 'Artikel erfolgreich hinzugefügt', + 'definition_successful_deleted' => 'Löschung erfolgreich', + 'definition_successful_updating' => 'Sie haben das Attributerfolgreich aktualisiert', + 'definition_type' => 'Attribut Typ', + 'definition_type_required' => 'Attribut ist ein Pflichtfeld', + 'definition_unit' => 'Maßeinheit', + 'definition_values' => 'Attribut Werte', + 'new' => 'Neues Attribut', + 'no_attributes_to_display' => 'Keine Elemente zum Anzeigen', + 'receipt_visibility' => 'Quittung', + 'show_in_items' => 'In Artikeln anzeigen', + 'show_in_items_visibility' => 'Artikel', + 'show_in_receipt' => 'In Quittung anzeigen', + 'show_in_receivings' => 'In Eingängen anzeigen', + 'show_in_receivings_visibility' => 'Eingänge', + 'show_in_sales' => 'In Verkäufen anzeigen', + 'show_in_sales_visibility' => 'Verkauf', + 'show_in_search' => 'In Suche anzeigen', + 'show_in_search_visibility' => 'Suche', + 'update' => 'Attribut aktualisieren', ]; diff --git a/app/Language/de-DE/Config.php b/app/Language/de-DE/Config.php index cccf7c4bd..b4effff6e 100644 --- a/app/Language/de-DE/Config.php +++ b/app/Language/de-DE/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Gespeicherte Nachricht', 'msg_msg_placeholder' => 'Wenn Sie eine SMS Vorlage benutzen wollen, geben Sie diese hier ein, ansonsten lassen Sie dieses Feld frei.', 'msg_pwd' => 'SMS-API Passwort', - 'msg_pwd_required' => 'SMS-API Passwort ist ein Pflichtfeld', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID ist ein Pflichtfeld', 'msg_uid' => 'SMS-API Benutzername', @@ -285,7 +284,7 @@ return [ 'right' => 'Rechts', 'sales_invoice_format' => 'Format Verkaufsrechnung', 'sales_quote_format' => 'Angebotsformat', - 'mailpath_invalid' => 'Ungültiger Sendmail-Pfad. Nur Buchstaben, Zahlen, Bindestriche, Unterstriche, Schrägstriche und Punkte sind erlaubt.', + 'mailpath_invalid' => 'Ungültiger Sendmail-Pfad. Nur Buchstaben, Zahlen, Bindestriche, Unterstriche, Schrägstriche, Rückwärtsschrägstriche, Doppelpunkte, Leerzeichen und Punkte sind erlaubt.', 'saved_successfully' => 'Einstellungen erfolgreich gesichert.', 'saved_unsuccessfully' => 'Einstellungen konnten nicht gesichert werden.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/de-DE/Error.php b/app/Language/de-DE/Error.php index 23b161b63..60b048711 100644 --- a/app/Language/de-DE/Error.php +++ b/app/Language/de-DE/Error.php @@ -1,6 +1,14 @@ "Sie haben nicht die Zugangsrechte für das gewählte Modul", - "unknown" => "Unbekannter Fehler", + 'no_permission_module' => 'Sie haben nicht die Zugangsrechte für das gewählte Modul', + 'unable_to_create_env_file' => '{filePath} konnte nicht erstellt werden, um throttle.key bereitzustellen', + 'unable_to_lock_file' => '{filePath} konnte nicht gesperrt werden: {reason}', + 'unable_to_open_lock_file' => '{filePath} konnte nicht geöffnet werden: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key konnte nicht in {filePath} gespeichert werden', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => '{filePath} konnte nicht gelesen werden, um throttle.key bereitzustellen', + 'unknown' => 'Unbekannter Fehler', ]; diff --git a/app/Language/el/Attributes.php b/app/Language/el/Attributes.php index 54a88f00e..f3d8f6792 100644 --- a/app/Language/el/Attributes.php +++ b/app/Language/el/Attributes.php @@ -1,34 +1,36 @@ "Η τιμή του χαρακτηριστικού δεν μπορεί να περιέχει ':' ή '|'", - "confirm_delete" => "Είστε βέβαιοι ότι θέλετε να διαγράψετε τα επιλεγμένα χαρακτηριστικά;", - "confirm_restore" => "Είστε βέβαιοι ότι θέλετε να επαναφέρετε τα επιλεγμένα χαρακτηριστικά;", - "definition_cannot_be_deleted" => "Δεν ήταν δυνατή η διαγραφή των επιλεγμένων χαρακτηριστικών", - "definition_invalid_group" => "Η επιλεγμένη ομάδα δεν υπάρχει ή δεν είναι έγκυρη.", - "definition_error_adding_updating" => "Το χαρακτηριστικό {0} δεν ήταν δυνατό να προστεθεί ή να ενημερωθεί. Ελέγξτε το αρχείο καταγραφής σφαλμάτων.", - "definition_flags" => "Ορατότητα χαρακτηριστικών", - "definition_group" => "Ομάδα", - "definition_id" => "Id", - "definition_name" => "Προσθήκη χαρακτηριστικού", - "definition_name_required" => "Το όνομα του χαρακτηριστικού είναι υποχρεωτικό πεδίο", - "definition_one_or_multiple" => "Χαρακτηριστικό(ά)", - "definition_successful_adding" => "Έχετε προσθέσει με επιτυχία στοιχείο", - "definition_successful_deleted" => "", - "definition_successful_updating" => "Έχετε ενημερώσει με επιτυχία το χαρακτηριστικό", - "definition_type" => "Τύπος Χαρακτηριστικού", - "definition_type_required" => "Ο τύπος ιδιότητας είναι υποχρεωτικό πεδίο", - "definition_unit" => "Μονάδα μέτρησης", - "definition_values" => "Τιμές Χαρακτηριστικών", - "new" => "Νέο χαρακτηριστικό", - "no_attributes_to_display" => "Δεν υπάρχουν στοιχεία για εμφάνιση", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => 'Η τιμή του χαρακτηριστικού δεν μπορεί να περιέχει \':\' ή \'|\'', + 'confirm_delete' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε τα επιλεγμένα χαρακτηριστικά;', + 'confirm_restore' => 'Είστε βέβαιοι ότι θέλετε να επαναφέρετε τα επιλεγμένα χαρακτηριστικά;', + 'definition_cannot_be_deleted' => 'Δεν ήταν δυνατή η διαγραφή των επιλεγμένων χαρακτηριστικών', + 'definition_invalid_group' => 'Η επιλεγμένη ομάδα δεν υπάρχει ή δεν είναι έγκυρη.', + 'definition_error_adding_updating' => 'Το χαρακτηριστικό {0} δεν ήταν δυνατό να προστεθεί ή να ενημερωθεί. Ελέγξτε το αρχείο καταγραφής σφαλμάτων.', + 'definition_flags' => 'Ορατότητα χαρακτηριστικών', + 'definition_group' => 'Ομάδα', + 'definition_id' => 'Id', + 'definition_name' => 'Προσθήκη χαρακτηριστικού', + 'definition_name_required' => 'Το όνομα του χαρακτηριστικού είναι υποχρεωτικό πεδίο', + 'definition_one_or_multiple' => 'Χαρακτηριστικό(ά)', + 'definition_successful_adding' => 'Έχετε προσθέσει με επιτυχία στοιχείο', + 'definition_successful_deleted' => '', + 'definition_successful_updating' => 'Έχετε ενημερώσει με επιτυχία το χαρακτηριστικό', + 'definition_type' => 'Τύπος Χαρακτηριστικού', + 'definition_type_required' => 'Ο τύπος ιδιότητας είναι υποχρεωτικό πεδίο', + 'definition_unit' => 'Μονάδα μέτρησης', + 'definition_values' => 'Τιμές Χαρακτηριστικών', + 'new' => 'Νέο χαρακτηριστικό', + 'no_attributes_to_display' => 'Δεν υπάρχουν στοιχεία για εμφάνιση', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'Εμφάνιση στην αναζήτηση', + 'show_in_search_visibility' => 'Αναζήτηση', + 'update' => '', ]; diff --git a/app/Language/el/Config.php b/app/Language/el/Config.php index a4d3aa00a..d102694e0 100644 --- a/app/Language/el/Config.php +++ b/app/Language/el/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Μη έγκυρη διαδρομή sendmail. Επιτρέπονται μόνο γράμματα, αριθμοί, παύλες, κάτω παύλες, κάθετοι, ανάστροφες κάθετοι, άνω-κάτω τελείες, κενά και τελείες.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/el/Error.php b/app/Language/el/Error.php index 64ebe1e41..4c81a6274 100644 --- a/app/Language/el/Error.php +++ b/app/Language/el/Error.php @@ -1,6 +1,14 @@ "", - "unknown" => "", + 'no_permission_module' => 'Δεν έχετε δικαίωμα πρόσβασης στη μονάδα με το όνομα', + 'unable_to_create_env_file' => 'Δεν ήταν δυνατή η δημιουργία του {filePath} για την παροχή του throttle.key', + 'unable_to_lock_file' => 'Δεν ήταν δυνατό το κλείδωμα του {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Δεν ήταν δυνατό το άνοιγμα του {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Δεν ήταν δυνατή η αποθήκευση του throttle.key στο {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Δεν ήταν δυνατή η ανάγνωση του {filePath} για την παροχή του throttle.key', + 'unknown' => 'Μη αναμενόμενο σφάλμα', ]; diff --git a/app/Language/el/Reports.php b/app/Language/el/Reports.php index 93316dfd2..15ff6bed9 100644 --- a/app/Language/el/Reports.php +++ b/app/Language/el/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/en-GB/Attributes.php b/app/Language/en-GB/Attributes.php index 0b4bf10cc..51775f7fe 100644 --- a/app/Language/en-GB/Attributes.php +++ b/app/Language/en-GB/Attributes.php @@ -1,34 +1,36 @@ "Attribute value cannot contain '_' or '|'", - "confirm_delete" => "Are you sure you want to delete the selected attribute(s)?", - "confirm_restore" => "Are you sure you want to restore the selected attribute(s)?", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_invalid_group" => "The selected group does not exist or is invalid.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'Attribute value cannot contain \'_\' or \'|\'', + 'confirm_delete' => 'Are you sure you want to delete the selected attribute(s)?', + 'confirm_restore' => 'Are you sure you want to restore the selected attribute(s)?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_invalid_group' => 'The selected group does not exist or is invalid.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute name is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated attribute', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute type is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Attributes to display', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'Show in search', + 'show_in_search_visibility' => 'Search', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/en-GB/Config.php b/app/Language/en-GB/Config.php index 5de11324b..e356369d2 100644 --- a/app/Language/en-GB/Config.php +++ b/app/Language/en-GB/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here. Otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -237,8 +236,8 @@ return [ 'print_bottom_margin_number' => 'Bottom Margin must be a number.', 'print_bottom_margin_required' => 'Bottom Margin is a required field.', 'print_delay_autoreturn' => 'Autoreturn to Sale delay', - 'print_delay_autoreturn_number' => 'Autoreturn to Sale delay is a required field.', - 'print_delay_autoreturn_required' => 'Autoreturn to Sale delay must be a number.', + 'print_delay_autoreturn_number' => 'Autoreturn to Sale delay must be a number.', + 'print_delay_autoreturn_required' => 'Autoreturn to Sale delay is a required field.', 'print_footer' => 'Print Browser Footer', 'print_header' => 'Print Browser Header', 'print_left_margin' => 'Margin Left', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => 'Invalid sendmail path. Only letters, numbers, dashes, underscores, slashes and dots are allowed.', + 'mailpath_invalid' => 'Invalid sendmail path. Only letters, numbers, dashes, underscores, slashes, backslashes, colons, spaces and dots are allowed.', 'saved_successfully' => 'Configuration saved successfully.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/en-GB/Error.php b/app/Language/en-GB/Error.php index e2e744336..29f0ba2f6 100644 --- a/app/Language/en-GB/Error.php +++ b/app/Language/en-GB/Error.php @@ -1,6 +1,14 @@ "You do not have the permission to access the module named", - "unknown" => "Unexpected error", + 'no_permission_module' => 'You do not have the permission to access the module named', + 'unable_to_create_env_file' => 'Unable to create {filePath} to provision an environment key', + 'unable_to_lock_file' => 'Unable to lock {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Unable to open {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Unable to persist throttle.key to {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Unable to read {filePath} to provision an environment key', + 'unknown' => 'Unexpected error', ]; diff --git a/app/Language/en/Attributes.php b/app/Language/en/Attributes.php index b3ab1fde9..51775f7fe 100644 --- a/app/Language/en/Attributes.php +++ b/app/Language/en/Attributes.php @@ -1,34 +1,36 @@ "Attribute value cannot contain '_' or '|'", - "confirm_delete" => "Are you sure you want to delete the selected attribute(s)?", - "confirm_restore" => "Are you sure you want to restore the selected attribute(s)?", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_invalid_group" => "The selected group does not exist or is invalid.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Attributes to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'Attribute value cannot contain \'_\' or \'|\'', + 'confirm_delete' => 'Are you sure you want to delete the selected attribute(s)?', + 'confirm_restore' => 'Are you sure you want to restore the selected attribute(s)?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_invalid_group' => 'The selected group does not exist or is invalid.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute name is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated attribute', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute type is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Attributes to display', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'Show in search', + 'show_in_search_visibility' => 'Search', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/en/Config.php b/app/Language/en/Config.php index 6ad5f033c..01c7c7c0c 100644 --- a/app/Language/en/Config.php +++ b/app/Language/en/Config.php @@ -119,6 +119,7 @@ return [ 'email_smtp_crypto' => 'SMTP Encryption', 'email_smtp_host' => 'SMTP Server', 'email_smtp_pass' => 'SMTP Password', + 'email_smtp_pass_set' => 'Already set — leave blank to keep', 'email_smtp_port' => 'SMTP Port', 'email_smtp_timeout' => 'SMTP Timeout (s)', 'email_smtp_user' => 'SMTP Username', @@ -200,6 +201,7 @@ return [ 'logout' => 'Do you want to make a backup before logging out? Click [OK] to backup or [Cancel] to logout.', 'mailchimp' => 'MailChimp', 'mailchimp_api_key' => 'MailChimp API Key', + 'mailchimp_api_key_set' => 'Already set — leave blank to keep', 'mailchimp_configuration' => 'MailChimp Configuration', 'mailchimp_key_successfully' => 'API Key is valid.', 'mailchimp_key_unsuccessfully' => 'API Key is invalid.', @@ -210,7 +212,7 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', + 'msg_pwd_set' => 'Already set — leave blank to keep', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -285,7 +287,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => 'Invalid sendmail path. Only letters, numbers, dashes, underscores, slashes and dots are allowed.', + 'mailpath_invalid' => 'Invalid sendmail path. Only letters, numbers, dashes, underscores, slashes, backslashes, colons, spaces and dots are allowed.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/en/Error.php b/app/Language/en/Error.php index ace67fbb1..930f88425 100644 --- a/app/Language/en/Error.php +++ b/app/Language/en/Error.php @@ -1,6 +1,14 @@ "You do not have permission to access the module named", - "unknown" => "Unexpected error", + 'no_permission_module' => 'You do not have permission to access the module named', + 'unable_to_create_env_file' => 'Unable to create {filePath} to provision an environment key', + 'unable_to_lock_file' => 'Unable to lock {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Unable to open {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Unable to persist throttle.key to {filePath}', + 'unable_to_persist_encryption_key' => 'Unable to persist the encryption key to {filePath}', + 'encryption_key_not_provisioned' => 'No encryption key is provisioned. Run `php spark env:provision` before continuing.', + 'throttle_key_not_provisioned' => 'No throttle key is provisioned. Run `php spark env:provision` before continuing.', + 'unable_to_read_env_file' => 'Unable to read {filePath} to provision an environment key', + 'unknown' => 'Unexpected error', ]; diff --git a/app/Language/en/Reports.php b/app/Language/en/Reports.php index d8361b7f6..9912da3cc 100644 --- a/app/Language/en/Reports.php +++ b/app/Language/en/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Tax Rate", "taxes" => "Taxes", "taxes_summary_report" => "Taxes Summary Report", + "toggle_cost_and_profit" => "Toggle Cost & Profit", "total" => "Total", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "Total Low Sell Quantity", @@ -146,5 +147,4 @@ return [ "used" => "Points Used", "work_orders" => "Work Orders", "zero_and_less" => "Zero and less", - "toggle_cost_and_profit" => "Toggle Cost & Profit", ]; diff --git a/app/Language/es-ES/Attributes.php b/app/Language/es-ES/Attributes.php index 6b05b71df..16996bf0f 100644 --- a/app/Language/es-ES/Attributes.php +++ b/app/Language/es-ES/Attributes.php @@ -1,34 +1,36 @@ "El valor del atributo no puede contener ':' o '|'", - "confirm_delete" => "¿Está seguro de que desea borrar los atributos seleccionados?", - "confirm_restore" => "¿Está seguro de que desea restaurar los atributos seleccionados?", - "definition_cannot_be_deleted" => "No se han podido borrar los atributos seleccionados", - "definition_invalid_group" => "El grupo seleccionado no existe o no es válido.", - "definition_error_adding_updating" => "El atributo {0} no pudo ser agregado o actulizado. Por favor compruebe el registro de errores.", - "definition_flags" => "Visibilidad del atributo", - "definition_group" => "Grupo", - "definition_id" => "Id", - "definition_name" => "Agregar Atributo", - "definition_name_required" => "El nombre del Atributo es un campo obligatorio", - "definition_one_or_multiple" => "atributo(s)", - "definition_successful_adding" => "Ha agregado con éxito el atributo", - "definition_successful_deleted" => "Ha eliminado con éxito", - "definition_successful_updating" => "Ha actualizado con éxito el atributo", - "definition_type" => "Tipo de Atributo", - "definition_type_required" => "Tipo de Atributo es un campo obligatorio", - "definition_unit" => "Unidad de medida", - "definition_values" => "Valores del Atributo", - "new" => "Nuevo Atributo", - "no_attributes_to_display" => "No hay Atributos para mostrar", - "receipt_visibility" => "Recibo", - "show_in_items" => "Mostrar en ítems", - "show_in_items_visibility" => "Ítems", - "show_in_receipt" => "Mostrar en recibo", - "show_in_receivings" => "Mostrar en recibos", - "show_in_receivings_visibility" => "Recibos", - "show_in_sales" => "Mostrar en ventas", - "show_in_sales_visibility" => "Ventas", - "update" => "Actualizar Atributo", + 'attribute_value_invalid_chars' => 'El valor del atributo no puede contener \':\' o \'|\'', + 'confirm_delete' => '¿Está seguro de que desea borrar los atributos seleccionados?', + 'confirm_restore' => '¿Está seguro de que desea restaurar los atributos seleccionados?', + 'definition_cannot_be_deleted' => 'No se han podido borrar los atributos seleccionados', + 'definition_invalid_group' => 'El grupo seleccionado no existe o no es válido.', + 'definition_error_adding_updating' => 'El atributo {0} no pudo ser agregado o actulizado. Por favor compruebe el registro de errores.', + 'definition_flags' => 'Visibilidad del atributo', + 'definition_group' => 'Grupo', + 'definition_id' => 'Id', + 'definition_name' => 'Agregar Atributo', + 'definition_name_required' => 'El nombre del Atributo es un campo obligatorio', + 'definition_one_or_multiple' => 'atributo(s)', + 'definition_successful_adding' => 'Ha agregado con éxito el atributo', + 'definition_successful_deleted' => 'Ha eliminado con éxito', + 'definition_successful_updating' => 'Ha actualizado con éxito el atributo', + 'definition_type' => 'Tipo de Atributo', + 'definition_type_required' => 'Tipo de Atributo es un campo obligatorio', + 'definition_unit' => 'Unidad de medida', + 'definition_values' => 'Valores del Atributo', + 'new' => 'Nuevo Atributo', + 'no_attributes_to_display' => 'No hay Atributos para mostrar', + 'receipt_visibility' => 'Recibo', + 'show_in_items' => 'Mostrar en ítems', + 'show_in_items_visibility' => 'Ítems', + 'show_in_receipt' => 'Mostrar en recibo', + 'show_in_receivings' => 'Mostrar en recibos', + 'show_in_receivings_visibility' => 'Recibos', + 'show_in_sales' => 'Mostrar en ventas', + 'show_in_sales_visibility' => 'Ventas', + 'show_in_search' => 'Mostrar en búsqueda', + 'show_in_search_visibility' => 'Búsqueda', + 'update' => 'Actualizar Atributo', ]; diff --git a/app/Language/es-ES/Config.php b/app/Language/es-ES/Config.php index 393b01675..eddd74b55 100644 --- a/app/Language/es-ES/Config.php +++ b/app/Language/es-ES/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Texto del mensaje guardado', 'msg_msg_placeholder' => 'Si desea usar un formato de SMS guarde su mensaje aquí, en caso contrario deje en blanco.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password es un campo requerido', 'msg_src' => 'SMS-API ID remitente', 'msg_src_required' => 'SMS-API ID remitente es un campo requerido', 'msg_uid' => 'SMS-API Usuario', @@ -285,7 +284,7 @@ return [ 'right' => 'Derecha', 'sales_invoice_format' => 'Formato de Facturas de Venta', 'sales_quote_format' => 'Formato de presupuesto de las ventas', - 'mailpath_invalid' => 'Ruta de sendmail inválida. Solo se permiten letras, números, guiones, guiones bajos, barras y puntos.', + 'mailpath_invalid' => 'Ruta de sendmail no válida. Solo se permiten letras, números, guiones, guiones bajos, barras, barras invertidas, dos puntos, espacios y puntos.', 'saved_successfully' => 'Configuración guardada satisfactoriamente.', 'saved_unsuccessfully' => 'Configuración no guardada.', 'security_issue' => 'Advertencia de vulnerabilidad de seguridad', diff --git a/app/Language/es-ES/Error.php b/app/Language/es-ES/Error.php index 7c7e21487..aa16e6303 100644 --- a/app/Language/es-ES/Error.php +++ b/app/Language/es-ES/Error.php @@ -1,6 +1,14 @@ "No tienes permiso para accesar el módulo llamado", - "unknown" => "desconocido", + 'no_permission_module' => 'No tienes permiso para accesar el módulo llamado', + 'unable_to_create_env_file' => 'No se pudo crear {filePath} para aprovisionar throttle.key', + 'unable_to_lock_file' => 'No se pudo bloquear {filePath}: {reason}', + 'unable_to_open_lock_file' => 'No se pudo abrir {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'No se pudo guardar throttle.key en {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'No se pudo leer {filePath} para aprovisionar throttle.key', + 'unknown' => 'desconocido', ]; diff --git a/app/Language/es-MX/Attributes.php b/app/Language/es-MX/Attributes.php index ff2619e73..86c5792dd 100644 --- a/app/Language/es-MX/Attributes.php +++ b/app/Language/es-MX/Attributes.php @@ -1,34 +1,36 @@ "El valor del atributo no puede contener ':' or '|'", - "confirm_delete" => "¿Está seguro de eliminar el/los atributo(s) seleccionado(s)?", - "confirm_restore" => "¿Está seguro que quiere restaurar los atributos seleccionados?", - "definition_cannot_be_deleted" => "No ha sido posible eliminar el/los atributo(s) seleccionado(s)", - "definition_invalid_group" => "El grupo seleccionado no existe o no es válido.", - "definition_error_adding_updating" => "El atributo {0} no pudo ser agregado o actualizado. Favor de revisar el registro de errorres.", - "definition_flags" => "Visibilidad del atributo", - "definition_group" => "Grupo", - "definition_id" => "Id", - "definition_name" => "Agregar Atributo", - "definition_name_required" => "El nombre del atributo es un campo requerido", - "definition_one_or_multiple" => "atributo(s)", - "definition_successful_adding" => "Has agregado un atributo correctamente", - "definition_successful_deleted" => "El atributo se ha eliminado correctamente", - "definition_successful_updating" => "El atributo se ha actualizado correctamente", - "definition_type" => "Tipo de atributo", - "definition_type_required" => "El tipo de atributo es un campo requerido", - "definition_unit" => "Unidad de Medida", - "definition_values" => "Valores del atributo", - "new" => "Nuevo atributo", - "no_attributes_to_display" => "Sin artículos para mostrar", - "receipt_visibility" => "Recibo", - "show_in_items" => "Mostrar en artículos", - "show_in_items_visibility" => "Artículos", - "show_in_receipt" => "Mostrar en recibo", - "show_in_receivings" => "Mostrar en recepciones", - "show_in_receivings_visibility" => "Recepciones", - "show_in_sales" => "Mostrar en Ventas", - "show_in_sales_visibility" => "Ventas", - "update" => "Actualizar atributo", + 'attribute_value_invalid_chars' => 'El valor del atributo no puede contener \':\' or \'|\'', + 'confirm_delete' => '¿Está seguro de eliminar el/los atributo(s) seleccionado(s)?', + 'confirm_restore' => '¿Está seguro que quiere restaurar los atributos seleccionados?', + 'definition_cannot_be_deleted' => 'No ha sido posible eliminar el/los atributo(s) seleccionado(s)', + 'definition_invalid_group' => 'El grupo seleccionado no existe o no es válido.', + 'definition_error_adding_updating' => 'El atributo {0} no pudo ser agregado o actualizado. Favor de revisar el registro de errorres.', + 'definition_flags' => 'Visibilidad del atributo', + 'definition_group' => 'Grupo', + 'definition_id' => 'Id', + 'definition_name' => 'Agregar Atributo', + 'definition_name_required' => 'El nombre del atributo es un campo requerido', + 'definition_one_or_multiple' => 'atributo(s)', + 'definition_successful_adding' => 'Has agregado un atributo correctamente', + 'definition_successful_deleted' => 'El atributo se ha eliminado correctamente', + 'definition_successful_updating' => 'El atributo se ha actualizado correctamente', + 'definition_type' => 'Tipo de atributo', + 'definition_type_required' => 'El tipo de atributo es un campo requerido', + 'definition_unit' => 'Unidad de Medida', + 'definition_values' => 'Valores del atributo', + 'new' => 'Nuevo atributo', + 'no_attributes_to_display' => 'Sin artículos para mostrar', + 'receipt_visibility' => 'Recibo', + 'show_in_items' => 'Mostrar en artículos', + 'show_in_items_visibility' => 'Artículos', + 'show_in_receipt' => 'Mostrar en recibo', + 'show_in_receivings' => 'Mostrar en recepciones', + 'show_in_receivings_visibility' => 'Recepciones', + 'show_in_sales' => 'Mostrar en Ventas', + 'show_in_sales_visibility' => 'Ventas', + 'show_in_search' => 'Mostrar en búsqueda', + 'show_in_search_visibility' => 'Búsqueda', + 'update' => 'Actualizar atributo', ]; diff --git a/app/Language/es-MX/Config.php b/app/Language/es-MX/Config.php index e3d34980f..987f62a8e 100644 --- a/app/Language/es-MX/Config.php +++ b/app/Language/es-MX/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -237,8 +236,8 @@ return [ 'print_bottom_margin_number' => 'Margin Bottom must be a number.', 'print_bottom_margin_required' => 'Margin Bottom is a required field.', 'print_delay_autoreturn' => 'Autoreturn to Sale delay', - 'print_delay_autoreturn_number' => 'Autoreturn to Sale delay is a required field.', - 'print_delay_autoreturn_required' => 'Autoreturn to Sale delay must be a number.', + 'print_delay_autoreturn_number' => '', + 'print_delay_autoreturn_required' => '', 'print_footer' => 'Print Browser Footer', 'print_header' => 'Print Browser Header', 'print_left_margin' => 'Margin Left', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => 'Ruta de sendmail inválida. Solo se permiten letras, números, guiones, guiones bajos, barras y puntos.', + 'mailpath_invalid' => 'Ruta de sendmail inválida. Solo se permiten letras, números, guiones, guiones bajos, diagonales, diagonales invertidas, dos puntos, espacios y puntos.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/es-MX/Error.php b/app/Language/es-MX/Error.php index c93630786..c477ea08e 100644 --- a/app/Language/es-MX/Error.php +++ b/app/Language/es-MX/Error.php @@ -1,6 +1,14 @@ "No tienes permiso para acceder el módulo llamado", - "unknown" => "Error inesperado", + 'no_permission_module' => 'No tienes permiso para acceder el módulo llamado', + 'unable_to_create_env_file' => 'No se pudo crear {filePath} para aprovisionar throttle.key', + 'unable_to_lock_file' => 'No se pudo bloquear {filePath}: {reason}', + 'unable_to_open_lock_file' => 'No se pudo abrir {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'No se pudo guardar throttle.key en {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'No se pudo leer {filePath} para aprovisionar throttle.key', + 'unknown' => 'Error inesperado', ]; diff --git a/app/Language/es-MX/Reports.php b/app/Language/es-MX/Reports.php index 4f24ae0f9..3f144f67b 100644 --- a/app/Language/es-MX/Reports.php +++ b/app/Language/es-MX/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Tarifa de Impuesto", "taxes" => "Impuestos", "taxes_summary_report" => "Reporte Resumido de Impuestos", + "toggle_cost_and_profit" => "Alternar Costo y Ganancia", "total" => "Total", "total_inventory_value" => "Valor Total del Inventario", "total_low_sell_quantity" => "Cantidad Total de Venta Baja", diff --git a/app/Language/fa/Attributes.php b/app/Language/fa/Attributes.php index 260c48963..174ac67dc 100644 --- a/app/Language/fa/Attributes.php +++ b/app/Language/fa/Attributes.php @@ -1,34 +1,36 @@ "مقدار ویژگی نمی تواند حاوی | یا ـ باشد", - "confirm_delete" => "آیا مطمئن هستید که می خواهید ویژگی (های) انتخاب شده را حذف کنید؟", - "confirm_restore" => "آیا مطمئن هستید که می خواهید ویژگی (های) انتخاب شده را بازیابی کنید؟", - "definition_cannot_be_deleted" => "نمی توان ویژگی (های) انتخابی را حذف کرد", - "definition_invalid_group" => "گروه انتخاب شده وجود ندارد یا نامعتبر است.", - "definition_error_adding_updating" => "ویژگی{0} اضافه نشد یا به روز نمی شود. لطفا گزارش خطا را بررسی کنید.", - "definition_flags" => "قابلیت مشاهده ویژگی", - "definition_group" => "گروه", - "definition_id" => "شناسه", - "definition_name" => "افزودن ویژگی", - "definition_name_required" => "نام ویژگی یک فیلد ضروری است", - "definition_one_or_multiple" => "(ویژگی(ها", - "definition_successful_adding" => "شما مورد را با موفقیت اضافه کردید", - "definition_successful_deleted" => "شما با موفقیت حذف شده اید", - "definition_successful_updating" => "شما ویژگی را با موفقیت به روز کردید", - "definition_type" => "نوع ویژگی", - "definition_type_required" => "نوع ویژگی یک فیلد ضروری است", - "definition_unit" => "واحد اندازه گیری", - "definition_values" => "مقادیر مشخصه", - "new" => "ویژگی جدید", - "no_attributes_to_display" => "هیچ موردی برای نمایش", - "receipt_visibility" => "اعلام وصول", - "show_in_items" => "نمایش در موارد", - "show_in_items_visibility" => "موارد", - "show_in_receipt" => "نمایش در رسید", - "show_in_receivings" => "نمایش در دریافت ها", - "show_in_receivings_visibility" => "دریافت", - "show_in_sales" => "نمایش در فروش", - "show_in_sales_visibility" => "حراجی", - "update" => "به روز کردن ویژگی", + 'attribute_value_invalid_chars' => 'مقدار ویژگی نمی تواند حاوی | یا ـ باشد', + 'confirm_delete' => 'آیا مطمئن هستید که می خواهید ویژگی (های) انتخاب شده را حذف کنید؟', + 'confirm_restore' => 'آیا مطمئن هستید که می خواهید ویژگی (های) انتخاب شده را بازیابی کنید؟', + 'definition_cannot_be_deleted' => 'نمی توان ویژگی (های) انتخابی را حذف کرد', + 'definition_invalid_group' => 'گروه انتخاب شده وجود ندارد یا نامعتبر است.', + 'definition_error_adding_updating' => 'ویژگی{0} اضافه نشد یا به روز نمی شود. لطفا گزارش خطا را بررسی کنید.', + 'definition_flags' => 'قابلیت مشاهده ویژگی', + 'definition_group' => 'گروه', + 'definition_id' => 'شناسه', + 'definition_name' => 'افزودن ویژگی', + 'definition_name_required' => 'نام ویژگی یک فیلد ضروری است', + 'definition_one_or_multiple' => '(ویژگی(ها', + 'definition_successful_adding' => 'شما مورد را با موفقیت اضافه کردید', + 'definition_successful_deleted' => 'شما با موفقیت حذف شده اید', + 'definition_successful_updating' => 'شما ویژگی را با موفقیت به روز کردید', + 'definition_type' => 'نوع ویژگی', + 'definition_type_required' => 'نوع ویژگی یک فیلد ضروری است', + 'definition_unit' => 'واحد اندازه گیری', + 'definition_values' => 'مقادیر مشخصه', + 'new' => 'ویژگی جدید', + 'no_attributes_to_display' => 'هیچ موردی برای نمایش', + 'receipt_visibility' => 'اعلام وصول', + 'show_in_items' => 'نمایش در موارد', + 'show_in_items_visibility' => 'موارد', + 'show_in_receipt' => 'نمایش در رسید', + 'show_in_receivings' => 'نمایش در دریافت ها', + 'show_in_receivings_visibility' => 'دریافت', + 'show_in_sales' => 'نمایش در فروش', + 'show_in_sales_visibility' => 'حراجی', + 'show_in_search' => 'نمایش در جستجو', + 'show_in_search_visibility' => 'جستجو', + 'update' => 'به روز کردن ویژگی', ]; diff --git a/app/Language/fa/Config.php b/app/Language/fa/Config.php index ff30aa060..fbc1a4bf1 100644 --- a/app/Language/fa/Config.php +++ b/app/Language/fa/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'پیام متنی ذخیره شده', 'msg_msg_placeholder' => 'اگر می خواهید از یک پیام کوتاه استفاده کنید ، پیام خود را در اینجا ذخیره کنید ، در غیر این صورت جعبه را خالی بگذارید.', 'msg_pwd' => 'گذرواژه SMS-API', - 'msg_pwd_required' => 'گذرواژه ای‌پی‌آی اس‌ام‌اس یک فیلد ضروری است', 'msg_src' => 'شناسه فرستنده ای‌پی‌آی اس‌ام‌اس ', 'msg_src_required' => 'شناسه فرستنده ای‌پی‌آی اس‌ام‌اس یک زمینه ضروری است', 'msg_uid' => 'نام کاربری ای‌پی‌آی اس‌ام‌اس ', @@ -285,7 +284,7 @@ return [ 'right' => 'درست', 'sales_invoice_format' => 'قالب فاکتور فروش', 'sales_quote_format' => 'قالب فروش قیمت', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'مسیر sendmail نامعتبر است. فقط حروف، اعداد، خط‌تیره، زیرخط، اسلش، بک‌اسلش، دونقطه، فاصله و نقطه مجاز هستند.', 'saved_successfully' => 'پیکربندی ذخیره موفقیت آمیز است.', 'saved_unsuccessfully' => 'ذخیره پیکربندی انجام نشد.', 'security_issue' => 'هشدار آسیب پذیری امنیتی', diff --git a/app/Language/fa/Error.php b/app/Language/fa/Error.php index 518640451..d3ce1666e 100644 --- a/app/Language/fa/Error.php +++ b/app/Language/fa/Error.php @@ -1,6 +1,14 @@ "شما اجازه دسترسی به ماژول به نام خود را ندارید", - "unknown" => "خطای غیر منتظره", + 'no_permission_module' => 'شما اجازه دسترسی به ماژول به نام خود را ندارید', + 'unable_to_create_env_file' => 'ایجاد {filePath} برای آماده‌سازی throttle.key ممکن نشد', + 'unable_to_lock_file' => 'قفل کردن {filePath} ممکن نشد: {reason}', + 'unable_to_open_lock_file' => 'باز کردن {filePath} ممکن نشد: {reason}', + 'unable_to_persist_throttle_key' => 'ذخیره throttle.key در {filePath} ممکن نشد', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'خواندن {filePath} برای آماده‌سازی throttle.key ممکن نشد', + 'unknown' => 'خطای غیر منتظره', ]; diff --git a/app/Language/fa/Reports.php b/app/Language/fa/Reports.php index 00aa80535..a879c9276 100644 --- a/app/Language/fa/Reports.php +++ b/app/Language/fa/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "نرخ مالیات", "taxes" => "مالیات", "taxes_summary_report" => "گزارش خلاصه مالیات", + "toggle_cost_and_profit" => "", "total" => "جمع", "total_inventory_value" => "ارزش کل موجودی", "total_low_sell_quantity" => "تعداد کم فروش کم", diff --git a/app/Language/fr/Attributes.php b/app/Language/fr/Attributes.php index 436273cef..f56bb63ef 100644 --- a/app/Language/fr/Attributes.php +++ b/app/Language/fr/Attributes.php @@ -1,34 +1,36 @@ "La valeur de l'attribut ne doit pas contenir '_' ou '|'", - "confirm_delete" => "Êtes-vous certain de vouloir supprimer le(s) attribut(s) sélectionné(s) ?", - "confirm_restore" => "Êtes-vous certain de vouloir restaurer le(s) attribut(s) sélectionné(s) ?", - "definition_cannot_be_deleted" => "Le(s) attribut(s) sélectionné(s) n'ont pas pu être supprimé(s)", - "definition_invalid_group" => "Le groupe sélectionné n'existe pas ou est invalide.", - "definition_error_adding_updating" => "L'attribut {0} n'a pas pu être ajouté ou mis à jour. Veuillez vérifier le journal d'erreurs.", - "definition_flags" => "Visibilité de l'attribut", - "definition_group" => "Groupe", - "definition_id" => "ID", - "definition_name" => "Ajouter un attribut", - "definition_name_required" => "Le nom de l'attribut est requis", - "definition_one_or_multiple" => "attribut(s)", - "definition_successful_adding" => "Vous avez ajouté l'article avec succès", - "definition_successful_deleted" => "Vous avez supprimé avec succès", - "definition_successful_updating" => "Vous avez mis-à-jour l'attribut avec succès", - "definition_type" => "Type d'attribut", - "definition_type_required" => "Le type d'attribut est requis", - "definition_unit" => "Unité de mesure", - "definition_values" => "Valeurs de l'attribut", - "new" => "Nouvel attribut", - "no_attributes_to_display" => "Aucun attribut à afficher", - "receipt_visibility" => "Reçu", - "show_in_items" => "Afficher dans les articles", - "show_in_items_visibility" => "Articles", - "show_in_receipt" => "Afficher sur le reçu", - "show_in_receivings" => "Afficher dans les réceptions", - "show_in_receivings_visibility" => "Réceptions", - "show_in_sales" => "Afficher dans les ventes", - "show_in_sales_visibility" => "Ventes", - "update" => "Mettre à jour l'attribut", + 'attribute_value_invalid_chars' => 'La valeur de l\'attribut ne doit pas contenir \'_\' ou \'|\'', + 'confirm_delete' => 'Êtes-vous certain de vouloir supprimer le(s) attribut(s) sélectionné(s) ?', + 'confirm_restore' => 'Êtes-vous certain de vouloir restaurer le(s) attribut(s) sélectionné(s) ?', + 'definition_cannot_be_deleted' => 'Le(s) attribut(s) sélectionné(s) n\'ont pas pu être supprimé(s)', + 'definition_invalid_group' => 'Le groupe sélectionné n\'existe pas ou est invalide.', + 'definition_error_adding_updating' => 'L\'attribut {0} n\'a pas pu être ajouté ou mis à jour. Veuillez vérifier le journal d\'erreurs.', + 'definition_flags' => 'Visibilité de l\'attribut', + 'definition_group' => 'Groupe', + 'definition_id' => 'ID', + 'definition_name' => 'Ajouter un attribut', + 'definition_name_required' => 'Le nom de l\'attribut est requis', + 'definition_one_or_multiple' => 'attribut(s)', + 'definition_successful_adding' => 'Vous avez ajouté l\'article avec succès', + 'definition_successful_deleted' => 'Vous avez supprimé avec succès', + 'definition_successful_updating' => 'Vous avez mis-à-jour l\'attribut avec succès', + 'definition_type' => 'Type d\'attribut', + 'definition_type_required' => 'Le type d\'attribut est requis', + 'definition_unit' => 'Unité de mesure', + 'definition_values' => 'Valeurs de l\'attribut', + 'new' => 'Nouvel attribut', + 'no_attributes_to_display' => 'Aucun attribut à afficher', + 'receipt_visibility' => 'Reçu', + 'show_in_items' => 'Afficher dans les articles', + 'show_in_items_visibility' => 'Articles', + 'show_in_receipt' => 'Afficher sur le reçu', + 'show_in_receivings' => 'Afficher dans les réceptions', + 'show_in_receivings_visibility' => 'Réceptions', + 'show_in_sales' => 'Afficher dans les ventes', + 'show_in_sales_visibility' => 'Ventes', + 'show_in_search' => 'Afficher dans la recherche', + 'show_in_search_visibility' => 'Recherche', + 'update' => 'Mettre à jour l\'attribut', ]; diff --git a/app/Language/fr/Config.php b/app/Language/fr/Config.php index d4831eefa..d5502712d 100644 --- a/app/Language/fr/Config.php +++ b/app/Language/fr/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Message texte enregistré', 'msg_msg_placeholder' => 'Si vous souhaitez utiliser un modèle de SMS, enregistrez votre message ici. Sinon, laisser la boîte en blanc.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password est un champ obligatoire', 'msg_src' => "ID de l'expéditeur de SMS-API", 'msg_src_required' => "L'ID de l'expéditeur de SMS-API est un champ obligatoire", 'msg_uid' => "Nom d'utilisateur de l'API SMS", @@ -285,7 +284,7 @@ return [ 'right' => 'Droite', 'sales_invoice_format' => 'Format de la facture de vente', 'sales_quote_format' => 'Format de devis de vente', - 'mailpath_invalid' => 'Chemin sendmail invalide. Seuls les lettres, chiffres, tirets, underscores, barres obliques et points sont autorisés.', + 'mailpath_invalid' => 'Chemin sendmail invalide. Seuls les lettres, chiffres, tirets, traits de soulignement, barres obliques, antislashs, deux-points, espaces et points sont autorisés.', 'saved_successfully' => 'Configuration enregistrer avec succès.', 'saved_unsuccessfully' => "L'enregistrement de configuration a échoué.", 'security_issue' => 'Avertissement de faille de sécurité', diff --git a/app/Language/fr/Error.php b/app/Language/fr/Error.php index 687d5f2f7..68ef77cb8 100644 --- a/app/Language/fr/Error.php +++ b/app/Language/fr/Error.php @@ -1,6 +1,14 @@ "Vous n'avez pas d'autorisation d'accès pour le module", - "unknown" => "inconnu", + 'no_permission_module' => 'Vous n\'avez pas d\'autorisation d\'accès pour le module', + 'unable_to_create_env_file' => 'Impossible de créer {filePath} pour provisionner throttle.key', + 'unable_to_lock_file' => 'Impossible de verrouiller {filePath} : {reason}', + 'unable_to_open_lock_file' => 'Impossible d\'ouvrir {filePath} : {reason}', + 'unable_to_persist_throttle_key' => 'Impossible d\'enregistrer throttle.key dans {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Impossible de lire {filePath} pour provisionner throttle.key', + 'unknown' => 'inconnu', ]; diff --git a/app/Language/he/Attributes.php b/app/Language/he/Attributes.php index 53980bc72..527d24ba7 100644 --- a/app/Language/he/Attributes.php +++ b/app/Language/he/Attributes.php @@ -1,34 +1,36 @@ "ערך השדה אינו יכול להכיל ':' או '|'", - "confirm_delete" => "האם אתה בטוח שברצונך למחוק את המאפיינים שנבחרו?", - "confirm_restore" => "האם אתה בטוח שברצונך לשחזר את המאפיינים שנבחרו?", - "definition_cannot_be_deleted" => "לא ניתן למחוק מאפיינים נבחר(ים)", - "definition_invalid_group" => "הקבוצה שנבחרה לא קיימת או אינה תקינה.", - "definition_error_adding_updating" => "לא ניתן להוסיף או לעדכן את הערך {0}. בדוק את יומן השגיאות.", - "definition_flags" => "מאפיין גלוי", - "definition_group" => "קבוצה", - "definition_id" => "מספר זיהוי", - "definition_name" => "הוסף מאפיין", - "definition_name_required" => "מאפיין שם הינו שדה חובה", - "definition_one_or_multiple" => "תכונה (תכונות)", - "definition_successful_adding" => "הוספת בהצלחה את הפריט", - "definition_successful_deleted" => "נמחק בהצלחה", - "definition_successful_updating" => "עדכנת בהצלחה את המאפיין", - "definition_type" => "סוג מאפיין", - "definition_type_required" => "שדה המאפיין הינו שדה חובה", - "definition_unit" => "יחידת מדידה", - "definition_values" => "ערכי המאפיין", - "new" => "מאפיין חדש", - "no_attributes_to_display" => "אין פריטים להצגה", - "receipt_visibility" => "קבלה", - "show_in_items" => "הצג בפריטים", - "show_in_items_visibility" => "פריטים", - "show_in_receipt" => "הצג בקבלה", - "show_in_receivings" => "הצג בקבלת סחורה", - "show_in_receivings_visibility" => "קבלת סחורה", - "show_in_sales" => "הצג במכירות", - "show_in_sales_visibility" => "מכירות", - "update" => "עדכן מאפיין", + 'attribute_value_invalid_chars' => 'ערך השדה אינו יכול להכיל \':\' או \'|\'', + 'confirm_delete' => 'האם אתה בטוח שברצונך למחוק את המאפיינים שנבחרו?', + 'confirm_restore' => 'האם אתה בטוח שברצונך לשחזר את המאפיינים שנבחרו?', + 'definition_cannot_be_deleted' => 'לא ניתן למחוק מאפיינים נבחר(ים)', + 'definition_invalid_group' => 'הקבוצה שנבחרה לא קיימת או אינה תקינה.', + 'definition_error_adding_updating' => 'לא ניתן להוסיף או לעדכן את הערך {0}. בדוק את יומן השגיאות.', + 'definition_flags' => 'מאפיין גלוי', + 'definition_group' => 'קבוצה', + 'definition_id' => 'מספר זיהוי', + 'definition_name' => 'הוסף מאפיין', + 'definition_name_required' => 'מאפיין שם הינו שדה חובה', + 'definition_one_or_multiple' => 'תכונה (תכונות)', + 'definition_successful_adding' => 'הוספת בהצלחה את הפריט', + 'definition_successful_deleted' => 'נמחק בהצלחה', + 'definition_successful_updating' => 'עדכנת בהצלחה את המאפיין', + 'definition_type' => 'סוג מאפיין', + 'definition_type_required' => 'שדה המאפיין הינו שדה חובה', + 'definition_unit' => 'יחידת מדידה', + 'definition_values' => 'ערכי המאפיין', + 'new' => 'מאפיין חדש', + 'no_attributes_to_display' => 'אין פריטים להצגה', + 'receipt_visibility' => 'קבלה', + 'show_in_items' => 'הצג בפריטים', + 'show_in_items_visibility' => 'פריטים', + 'show_in_receipt' => 'הצג בקבלה', + 'show_in_receivings' => 'הצג בקבלת סחורה', + 'show_in_receivings_visibility' => 'קבלת סחורה', + 'show_in_sales' => 'הצג במכירות', + 'show_in_sales_visibility' => 'מכירות', + 'show_in_search' => 'הצג בחיפוש', + 'show_in_search_visibility' => 'חיפוש', + 'update' => 'עדכן מאפיין', ]; diff --git a/app/Language/he/Config.php b/app/Language/he/Config.php index b0690db67..b98c2c9c5 100644 --- a/app/Language/he/Config.php +++ b/app/Language/he/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'הודעת טקסט שמורה', 'msg_msg_placeholder' => 'אם ברצונך להשתמש בתבנית הודעה שמור את ההודעה שלך כאן, אחרת השאר את התיבה ריקה.', 'msg_pwd' => 'סיסמה של SMS-API', - 'msg_pwd_required' => 'סיסמה של SMS-API הינו שדה חובה', 'msg_src' => 'מזהה שולח SMS-API', 'msg_src_required' => 'מזהה שולח SMS-API הינו שדה חובה', 'msg_uid' => 'שם משתמש של SMS-API', @@ -285,7 +284,7 @@ return [ 'right' => 'ימין', 'sales_invoice_format' => 'תבנית חשבונית מכירות', 'sales_quote_format' => 'תבנית חשבונית הצעת מחיר', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'נתיב sendmail לא תקין. מותרים רק אותיות, ספרות, מקפים, קווים תחתונים, לוכסנים, לוכסנים הפוכים, נקודתיים, רווחים ונקודות.', 'saved_successfully' => 'ההגדרות נשמרו בהצלחה.', 'saved_unsuccessfully' => 'שמירת ההגדרות נכשלה.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/he/Error.php b/app/Language/he/Error.php index a6c9f59d6..65f5b97f5 100644 --- a/app/Language/he/Error.php +++ b/app/Language/he/Error.php @@ -1,6 +1,14 @@ "אין לך הרשאה לגשת אל המודול ששמו", - "unknown" => "שגיאה לא ידועה", + 'no_permission_module' => 'אין לך הרשאה לגשת אל המודול ששמו', + 'unable_to_create_env_file' => 'לא ניתן ליצור את {filePath} כדי להגדיר את throttle.key', + 'unable_to_lock_file' => 'לא ניתן לנעול את {filePath}: {reason}', + 'unable_to_open_lock_file' => 'לא ניתן לפתוח את {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'לא ניתן לשמור את throttle.key בקובץ {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'לא ניתן לקרוא את {filePath} כדי להגדיר את throttle.key', + 'unknown' => 'שגיאה לא ידועה', ]; diff --git a/app/Language/he/Reports.php b/app/Language/he/Reports.php index 393a6ef63..6177843b5 100644 --- a/app/Language/he/Reports.php +++ b/app/Language/he/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "שיעור מס", "taxes" => "מסים", "taxes_summary_report" => "דוח סיכום מסים", + "toggle_cost_and_profit" => "", "total" => "סהכ", "total_inventory_value" => "סהכ ערך מלאי", "total_low_sell_quantity" => "סהכ מינימום כמות למכירה", diff --git a/app/Language/hr-HR/Attributes.php b/app/Language/hr-HR/Attributes.php index 3213b12e4..a328dd205 100644 --- a/app/Language/hr-HR/Attributes.php +++ b/app/Language/hr-HR/Attributes.php @@ -1,34 +1,36 @@ "", - "confirm_delete" => "", - "confirm_restore" => "", - "definition_cannot_be_deleted" => "", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "", - "definition_flags" => "", - "definition_group" => "", - "definition_id" => "", - "definition_name" => "", - "definition_name_required" => "", - "definition_one_or_multiple" => "", - "definition_successful_adding" => "", - "definition_successful_deleted" => "", - "definition_successful_updating" => "", - "definition_type" => "", - "definition_type_required" => "", - "definition_unit" => "", - "definition_values" => "", - "new" => "", - "no_attributes_to_display" => "", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => '', + 'confirm_delete' => '', + 'confirm_restore' => '', + 'definition_cannot_be_deleted' => '', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => '', + 'definition_flags' => '', + 'definition_group' => '', + 'definition_id' => '', + 'definition_name' => '', + 'definition_name_required' => '', + 'definition_one_or_multiple' => '', + 'definition_successful_adding' => '', + 'definition_successful_deleted' => '', + 'definition_successful_updating' => '', + 'definition_type' => '', + 'definition_type_required' => '', + 'definition_unit' => '', + 'definition_values' => '', + 'new' => '', + 'no_attributes_to_display' => '', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'Prikaži u pretrazi', + 'show_in_search_visibility' => 'Pretraga', + 'update' => '', ]; diff --git a/app/Language/hr-HR/Config.php b/app/Language/hr-HR/Config.php index d82d87980..5cd2ee098 100644 --- a/app/Language/hr-HR/Config.php +++ b/app/Language/hr-HR/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here. Otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Oblik fakture', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Nevažeća sendmail putanja. Dopuštena su samo slova, brojevi, crtice, podvlake, kose crte, obrnute kose crte, dvotočke, razmaci i točke.', 'saved_successfully' => 'Konfiguracija je uspješno snimljena', 'saved_unsuccessfully' => 'Konfiguracija nije uspješno snimljena', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/hr-HR/Error.php b/app/Language/hr-HR/Error.php index 8cd38c12d..7485cce0d 100644 --- a/app/Language/hr-HR/Error.php +++ b/app/Language/hr-HR/Error.php @@ -1,6 +1,14 @@ "Nemate dozvolu za pristup modulu", - "unknown" => "ismeretlen", + 'no_permission_module' => 'Nemate dozvolu za pristup modulu', + 'unable_to_create_env_file' => 'Nije moguće izraditi {filePath} za postavljanje throttle.key', + 'unable_to_lock_file' => 'Nije moguće zaključati {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Nije moguće otvoriti {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Nije moguće trajno spremiti throttle.key u {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Nije moguće pročitati {filePath} za postavljanje throttle.key', + 'unknown' => 'Neočekivana pogreška', ]; diff --git a/app/Language/hr-HR/Reports.php b/app/Language/hr-HR/Reports.php index aa88f1803..53cecab8e 100644 --- a/app/Language/hr-HR/Reports.php +++ b/app/Language/hr-HR/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "Porezi", "taxes_summary_report" => "Zbrojni izvještaj po porezima", + "toggle_cost_and_profit" => "", "total" => "Ukupno", "total_inventory_value" => "Ukupan iznos inventure", "total_low_sell_quantity" => "", diff --git a/app/Language/hu/Attributes.php b/app/Language/hu/Attributes.php index 530aba7b4..e6fd036c9 100644 --- a/app/Language/hu/Attributes.php +++ b/app/Language/hu/Attributes.php @@ -1,34 +1,36 @@ "Tulajdonság értéke nem tartalmazhat '_' vagy '|' karaktert", - "confirm_delete" => "Biztosan törli szeretné a kijelölt tulajdonságokat?", - "confirm_restore" => "Biztosan visszaállítja a kijelölt tulajdonságokat?", - "definition_cannot_be_deleted" => "Nem sikerült törölni a kijelölt tulajdonságokat", - "definition_invalid_group" => "A kiválasztott csoport nem létezik vagy érvénytelen.", - "definition_error_adding_updating" => "{0} attribútum nem adható hozzá és nem frissíthető. Kérjük, ellenőrizze a hibanaplót.", - "definition_flags" => "Tulajdonság láthatósága", - "definition_group" => "Csoport", - "definition_id" => "Azonosító", - "definition_name" => "Tulajdonság hozzáadása", - "definition_name_required" => "Tulajdonság név kötelező mező", - "definition_one_or_multiple" => "tulajdonságok", - "definition_successful_adding" => "Sikeresen hozzáadta az elemet", - "definition_successful_deleted" => "Sikeresen törölte", - "definition_successful_updating" => "Sikeresen frissítette a tulajdonságot", - "definition_type" => "Tulajdonság típus", - "definition_type_required" => "Tulajdonság típusa kötelező mező", - "definition_unit" => "Mértkékegység", - "definition_values" => "Tulajdonság értékei", - "new" => "Új tulajdnoság", - "no_attributes_to_display" => "Nincs megjelenítendő elem", - "receipt_visibility" => "Nyugta", - "show_in_items" => "Megjelenítés a termékekben", - "show_in_items_visibility" => "Termékek", - "show_in_receipt" => "Nyugta megjelenítése", - "show_in_receivings" => "Meglenesítés árúátvételekben", - "show_in_receivings_visibility" => "Áruátvételek", - "show_in_sales" => "Megjelenítés az értékesítésekben", - "show_in_sales_visibility" => "Értékesítések", - "update" => "Tulajdonság frissítése", + 'attribute_value_invalid_chars' => 'Tulajdonság értéke nem tartalmazhat \'_\' vagy \'|\' karaktert', + 'confirm_delete' => 'Biztosan törli szeretné a kijelölt tulajdonságokat?', + 'confirm_restore' => 'Biztosan visszaállítja a kijelölt tulajdonságokat?', + 'definition_cannot_be_deleted' => 'Nem sikerült törölni a kijelölt tulajdonságokat', + 'definition_invalid_group' => 'A kiválasztott csoport nem létezik vagy érvénytelen.', + 'definition_error_adding_updating' => '{0} attribútum nem adható hozzá és nem frissíthető. Kérjük, ellenőrizze a hibanaplót.', + 'definition_flags' => 'Tulajdonság láthatósága', + 'definition_group' => 'Csoport', + 'definition_id' => 'Azonosító', + 'definition_name' => 'Tulajdonság hozzáadása', + 'definition_name_required' => 'Tulajdonság név kötelező mező', + 'definition_one_or_multiple' => 'tulajdonságok', + 'definition_successful_adding' => 'Sikeresen hozzáadta az elemet', + 'definition_successful_deleted' => 'Sikeresen törölte', + 'definition_successful_updating' => 'Sikeresen frissítette a tulajdonságot', + 'definition_type' => 'Tulajdonság típus', + 'definition_type_required' => 'Tulajdonság típusa kötelező mező', + 'definition_unit' => 'Mértkékegység', + 'definition_values' => 'Tulajdonság értékei', + 'new' => 'Új tulajdnoság', + 'no_attributes_to_display' => 'Nincs megjelenítendő elem', + 'receipt_visibility' => 'Nyugta', + 'show_in_items' => 'Megjelenítés a termékekben', + 'show_in_items_visibility' => 'Termékek', + 'show_in_receipt' => 'Nyugta megjelenítése', + 'show_in_receivings' => 'Meglenesítés árúátvételekben', + 'show_in_receivings_visibility' => 'Áruátvételek', + 'show_in_sales' => 'Megjelenítés az értékesítésekben', + 'show_in_sales_visibility' => 'Értékesítések', + 'show_in_search' => 'Megjelenítés a keresésben', + 'show_in_search_visibility' => 'Keresés', + 'update' => 'Tulajdonság frissítése', ]; diff --git a/app/Language/hu/Config.php b/app/Language/hu/Config.php index e9db17b3f..384557376 100644 --- a/app/Language/hu/Config.php +++ b/app/Language/hu/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Mentett text üzenet', 'msg_msg_placeholder' => 'Ha SMS alapot kíván használni ide írja. Egyébként hagyja üresen a mezőt.', 'msg_pwd' => 'SMS-API Jelszó', - 'msg_pwd_required' => 'SMS-API jelszó kötelező mező', 'msg_src' => 'SMS-API Küldö ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Felhasználó', @@ -285,7 +284,7 @@ return [ 'right' => 'Jobb', 'sales_invoice_format' => 'Eladási számla formátum', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Érvénytelen sendmail elérési út. Csak betűk, számok, kötőjelek, aláhúzások, perjelek, fordított perjelek, kettőspontok, szóközök és pontok engedélyezettek.', 'saved_successfully' => 'Beállítások sikeresen elmentve', 'saved_unsuccessfully' => 'Beállítások mentése sikertelen', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/hu/Error.php b/app/Language/hu/Error.php index 49b193503..733dc71dd 100644 --- a/app/Language/hu/Error.php +++ b/app/Language/hu/Error.php @@ -1,6 +1,14 @@ "Önnek nincs hozzáférése az alábbi modulhoz ", - "unknown" => "ismeretlen", + 'no_permission_module' => 'Önnek nincs hozzáférése az alábbi modulhoz ', + 'unable_to_create_env_file' => 'Nem sikerült létrehozni a(z) {filePath} fájlt a throttle.key beállításához', + 'unable_to_lock_file' => 'Nem sikerült zárolni a(z) {filePath} fájlt: {reason}', + 'unable_to_open_lock_file' => 'Nem sikerült megnyitni a(z) {filePath} fájlt: {reason}', + 'unable_to_persist_throttle_key' => 'Nem sikerült elmenteni a throttle.key kulcsot a(z) {filePath} fájlba', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Nem sikerült beolvasni a(z) {filePath} fájlt a throttle.key beállításához', + 'unknown' => 'ismeretlen', ]; diff --git a/app/Language/hu/Reports.php b/app/Language/hu/Reports.php index faee19987..b6310fee3 100644 --- a/app/Language/hu/Reports.php +++ b/app/Language/hu/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "Adók", "taxes_summary_report" => "Adók összegző riport", + "toggle_cost_and_profit" => "", "total" => "Összesen", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "", diff --git a/app/Language/hy/Attributes.php b/app/Language/hy/Attributes.php index 377b84f4b..e52e89f11 100644 --- a/app/Language/hy/Attributes.php +++ b/app/Language/hy/Attributes.php @@ -1,34 +1,36 @@ "Attribute value cannot contain ':' or '|'", - "confirm_delete" => "Are you sure you want to delete the selected attribute(s)?", - "confirm_restore" => "Are you sure you want to restore the selected attribute(s)?", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'Attribute value cannot contain \':\' or \'|\'', + 'confirm_delete' => 'Are you sure you want to delete the selected attribute(s)?', + 'confirm_restore' => 'Are you sure you want to restore the selected attribute(s)?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute name is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated attribute', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute type is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Items to display', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'Ցուցադրել որոնման մեջ', + 'show_in_search_visibility' => 'Որոնում', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/hy/Config.php b/app/Language/hy/Config.php index 54fedb971..2cc22ade7 100644 --- a/app/Language/hy/Config.php +++ b/app/Language/hy/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Sendmail-ի ուղին անվավեր է։ Թույլատրվում են միայն տառեր, թվեր, գծիկներ, ընդգծումներ, թեք գծեր, հակառակ թեք գծեր, երկկետեր, բացատներ և կետեր։', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => '', diff --git a/app/Language/hy/Error.php b/app/Language/hy/Error.php index 64ebe1e41..da0821306 100644 --- a/app/Language/hy/Error.php +++ b/app/Language/hy/Error.php @@ -1,6 +1,14 @@ "", - "unknown" => "", + 'no_permission_module' => 'Ձեզ չի թույլատրվում մուտք գործել այս անունով մոդուլ', + 'unable_to_create_env_file' => 'Հնարավոր չէ ստեղծել {filePath} ֆայլը՝ throttle.key-ը կարգավորելու համար', + 'unable_to_lock_file' => 'Հնարավոր չէ արգելափակել {filePath} ֆայլը՝ {reason}', + 'unable_to_open_lock_file' => 'Հնարավոր չէ բացել {filePath} ֆայլը՝ {reason}', + 'unable_to_persist_throttle_key' => 'Հնարավոր չէ պահպանել throttle.key-ը {filePath} ֆայլում', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Հնարավոր չէ կարդալ {filePath} ֆայլը՝ throttle.key-ը կարգավորելու համար', + 'unknown' => 'Անսպասելի սխալ', ]; diff --git a/app/Language/hy/Reports.php b/app/Language/hy/Reports.php index 93316dfd2..15ff6bed9 100644 --- a/app/Language/hy/Reports.php +++ b/app/Language/hy/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/id/Attributes.php b/app/Language/id/Attributes.php index 0d94527a0..d23be2e08 100644 --- a/app/Language/id/Attributes.php +++ b/app/Language/id/Attributes.php @@ -1,34 +1,36 @@ "Nilai Atribut tidak boleh mengandung karakter '_' atau '|'", - "confirm_delete" => "Apakah Anda yakin ingin menghapus atribut tersebut?", - "confirm_restore" => "Apakah Anda yakin ingin mengembalikan atribut tersebut?", - "definition_cannot_be_deleted" => "Tidak bisa menghapus atribut terpilih", - "definition_invalid_group" => "Grup yang dipilih tidak ada atau tidak valid.", - "definition_error_adding_updating" => "Atribut {0} tidak dapat ditambah atau diperbaharui. Silahkan periksa log kesalahan.", - "definition_flags" => "Visibilitas Atribut", - "definition_group" => "Grup", - "definition_id" => "Nomor Id", - "definition_name" => "Tambah Atribut", - "definition_name_required" => "Nama atribut harus diisi", - "definition_one_or_multiple" => "atribut", - "definition_successful_adding" => "Anda telah berhasil menambahkan atribut", - "definition_successful_deleted" => "Anda telah berhasil menghapus", - "definition_successful_updating" => "Anda telah berhasil memperbaharui atribut", - "definition_type" => "Tipe Atribut", - "definition_type_required" => "Tipe Atribut harus diisi", - "definition_unit" => "Satuan Ukuran", - "definition_values" => "Nilai Atribut", - "new" => "Atribut baru", - "no_attributes_to_display" => "Tidak ada Item yang dapat ditampilkan", - "receipt_visibility" => "Struk", - "show_in_items" => "Tampilkan dalam item", - "show_in_items_visibility" => "Item", - "show_in_receipt" => "Tampilkan dalam struk", - "show_in_receivings" => "Tampilkan dalam penerimaan", - "show_in_receivings_visibility" => "Penerimaan", - "show_in_sales" => "Tampilkan dalam penjualan", - "show_in_sales_visibility" => "Penjualan", - "update" => "Perbarui Atribut", + 'attribute_value_invalid_chars' => 'Nilai Atribut tidak boleh mengandung karakter \'_\' atau \'|\'', + 'confirm_delete' => 'Apakah Anda yakin ingin menghapus atribut tersebut?', + 'confirm_restore' => 'Apakah Anda yakin ingin mengembalikan atribut tersebut?', + 'definition_cannot_be_deleted' => 'Tidak bisa menghapus atribut terpilih', + 'definition_invalid_group' => 'Grup yang dipilih tidak ada atau tidak valid.', + 'definition_error_adding_updating' => 'Atribut {0} tidak dapat ditambah atau diperbaharui. Silahkan periksa log kesalahan.', + 'definition_flags' => 'Visibilitas Atribut', + 'definition_group' => 'Grup', + 'definition_id' => 'Nomor Id', + 'definition_name' => 'Tambah Atribut', + 'definition_name_required' => 'Nama atribut harus diisi', + 'definition_one_or_multiple' => 'atribut', + 'definition_successful_adding' => 'Anda telah berhasil menambahkan atribut', + 'definition_successful_deleted' => 'Anda telah berhasil menghapus', + 'definition_successful_updating' => 'Anda telah berhasil memperbaharui atribut', + 'definition_type' => 'Tipe Atribut', + 'definition_type_required' => 'Tipe Atribut harus diisi', + 'definition_unit' => 'Satuan Ukuran', + 'definition_values' => 'Nilai Atribut', + 'new' => 'Atribut baru', + 'no_attributes_to_display' => 'Tidak ada Item yang dapat ditampilkan', + 'receipt_visibility' => 'Struk', + 'show_in_items' => 'Tampilkan dalam item', + 'show_in_items_visibility' => 'Item', + 'show_in_receipt' => 'Tampilkan dalam struk', + 'show_in_receivings' => 'Tampilkan dalam penerimaan', + 'show_in_receivings_visibility' => 'Penerimaan', + 'show_in_sales' => 'Tampilkan dalam penjualan', + 'show_in_sales_visibility' => 'Penjualan', + 'show_in_search' => 'Tampilkan dalam pencarian', + 'show_in_search_visibility' => 'Pencarian', + 'update' => 'Perbarui Atribut', ]; diff --git a/app/Language/id/Config.php b/app/Language/id/Config.php index c0ab39129..c59d4c44e 100644 --- a/app/Language/id/Config.php +++ b/app/Language/id/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Pesan teks tersimpan', 'msg_msg_placeholder' => 'Apakah Anda ingin menggunakan template SMS menyimpan pesan Anda disini? Jika tidak, biarkan kosong.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password harus diisi', 'msg_src' => 'ID pengirim SMS-API', 'msg_src_required' => 'SMS-API Sender ID harus diisi', 'msg_uid' => 'SMS-API User Name', @@ -285,6 +284,7 @@ return [ 'right' => 'Kanan', 'sales_invoice_format' => 'Format Faktur Penjualan', 'sales_quote_format' => 'Format Penawaran Penjualan', + 'mailpath_invalid' => 'Jalur sendmail tidak valid. Hanya huruf, angka, tanda hubung, garis bawah, garis miring, garis miring terbalik, titik dua, spasi, dan titik yang diizinkan.', 'saved_successfully' => 'Konfigurasi berhasil disimpan.', 'saved_unsuccessfully' => 'Konfigurasi tidak berhasil disimpan.', 'security_issue' => 'Peringatan Kerentanan Keamanan', diff --git a/app/Language/id/Error.php b/app/Language/id/Error.php index 54a3924d9..aeea78f34 100644 --- a/app/Language/id/Error.php +++ b/app/Language/id/Error.php @@ -1,6 +1,14 @@ "Anda tidak memiliki izin untuk mengakses modul ini", - "unknown" => "tidak dikenal", + 'no_permission_module' => 'Anda tidak memiliki izin untuk mengakses modul ini', + 'unable_to_create_env_file' => 'Tidak dapat membuat {filePath} untuk menyediakan throttle.key', + 'unable_to_lock_file' => 'Tidak dapat mengunci {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Tidak dapat membuka {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Tidak dapat menyimpan throttle.key ke {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Tidak dapat membaca {filePath} untuk menyediakan throttle.key', + 'unknown' => 'tidak dikenal', ]; diff --git a/app/Language/id/Reports.php b/app/Language/id/Reports.php index 50f67eaf8..f6f0c6caa 100644 --- a/app/Language/id/Reports.php +++ b/app/Language/id/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Tarif Pajak", "taxes" => "Pajak", "taxes_summary_report" => "Laporan Ringkasan Pajak", + "toggle_cost_and_profit" => "", "total" => "Total", "total_inventory_value" => "Total Nilai Persediaan", "total_low_sell_quantity" => "Total Jumlah Penjualan yang Rendah", diff --git a/app/Language/it/Attributes.php b/app/Language/it/Attributes.php index d6c997eae..53e41620f 100644 --- a/app/Language/it/Attributes.php +++ b/app/Language/it/Attributes.php @@ -1,34 +1,36 @@ "Il valore dell'attributo non può contenere '_' o '|'", - "confirm_delete" => "Sei sicuro di voler eliminare gli attributi selezionati?", - "confirm_restore" => "Sei sicuro di voler ripristinare l'attributo selezionato?", - "definition_cannot_be_deleted" => "Non riesco a cancellare l'attributo selezionato", - "definition_invalid_group" => "Il gruppo selezionato non esiste o non è valido.", - "definition_error_adding_updating" => "Impossibile aggiungere o aggiornare l'attributo {0}. Si prega di controllare il registro degli errori.", - "definition_flags" => "Visibilità attributo", - "definition_group" => "Gruppo", - "definition_id" => "Id", - "definition_name" => "Aggiungi attributo", - "definition_name_required" => "Nome attributo è richiesto", - "definition_one_or_multiple" => "attributo(i)", - "definition_successful_adding" => "Hai aggiunto il prodotto con successo", - "definition_successful_deleted" => "Cancellato con successo", - "definition_successful_updating" => "Hai aggiornato correttamente l'attributo", - "definition_type" => "Tipo di attributo", - "definition_type_required" => "Il tipo di attribuito è richiesto", - "definition_unit" => "Unità di misura", - "definition_values" => "Valore attributo", - "new" => "Nuovo attributo", - "no_attributes_to_display" => "Nessun elemento da visualizzare", - "receipt_visibility" => "Scontrino", - "show_in_items" => "Visualizza in articoli", - "show_in_items_visibility" => "Articoli", - "show_in_receipt" => "Mostra in ricevuta", - "show_in_receivings" => "Mostra negli incassi", - "show_in_receivings_visibility" => "Ricezione", - "show_in_sales" => "Visualizza in vendite", - "show_in_sales_visibility" => "Vendite", - "update" => "Aggiorna attributo", + 'attribute_value_invalid_chars' => 'Il valore dell\'attributo non può contenere \'_\' o \'|\'', + 'confirm_delete' => 'Sei sicuro di voler eliminare gli attributi selezionati?', + 'confirm_restore' => 'Sei sicuro di voler ripristinare l\'attributo selezionato?', + 'definition_cannot_be_deleted' => 'Non riesco a cancellare l\'attributo selezionato', + 'definition_invalid_group' => 'Il gruppo selezionato non esiste o non è valido.', + 'definition_error_adding_updating' => 'Impossibile aggiungere o aggiornare l\'attributo {0}. Si prega di controllare il registro degli errori.', + 'definition_flags' => 'Visibilità attributo', + 'definition_group' => 'Gruppo', + 'definition_id' => 'Id', + 'definition_name' => 'Aggiungi attributo', + 'definition_name_required' => 'Nome attributo è richiesto', + 'definition_one_or_multiple' => 'attributo(i)', + 'definition_successful_adding' => 'Hai aggiunto il prodotto con successo', + 'definition_successful_deleted' => 'Cancellato con successo', + 'definition_successful_updating' => 'Hai aggiornato correttamente l\'attributo', + 'definition_type' => 'Tipo di attributo', + 'definition_type_required' => 'Il tipo di attribuito è richiesto', + 'definition_unit' => 'Unità di misura', + 'definition_values' => 'Valore attributo', + 'new' => 'Nuovo attributo', + 'no_attributes_to_display' => 'Nessun elemento da visualizzare', + 'receipt_visibility' => 'Scontrino', + 'show_in_items' => 'Visualizza in articoli', + 'show_in_items_visibility' => 'Articoli', + 'show_in_receipt' => 'Mostra in ricevuta', + 'show_in_receivings' => 'Mostra negli incassi', + 'show_in_receivings_visibility' => 'Ricezione', + 'show_in_sales' => 'Visualizza in vendite', + 'show_in_sales_visibility' => 'Vendite', + 'show_in_search' => 'Visualizza nella ricerca', + 'show_in_search_visibility' => 'Ricerca', + 'update' => 'Aggiorna attributo', ]; diff --git a/app/Language/it/Config.php b/app/Language/it/Config.php index 68ef1dc4b..e5cce9eec 100644 --- a/app/Language/it/Config.php +++ b/app/Language/it/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Testo Messaggio Salvato', 'msg_msg_placeholder' => 'Se vuoi utilizzare un template SMS, salva il tuo messaggio qui o lascia il campo in bianco.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password è un campo obbligatorio', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID è un campo obbligatorio', 'msg_uid' => 'SMS-API Username', @@ -285,7 +284,7 @@ return [ 'right' => 'Destra', 'sales_invoice_format' => 'Formato Fattura di Vendita', 'sales_quote_format' => 'Formato Preventivo', - 'mailpath_invalid' => 'Percorso sendmail non valido. Sono ammessi solo lettere, numeri, trattini, trattini bassi, barre e punti.', + 'mailpath_invalid' => 'Percorso sendmail non valido. Sono ammessi solo lettere, numeri, trattini, trattini bassi, barre, barre rovesciate, due punti, spazi e punti.', 'saved_successfully' => 'Configurazione salvata correttamente.', 'saved_unsuccessfully' => 'Salvataggio Configurazione Fallito.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/it/Error.php b/app/Language/it/Error.php index 0e9f47f80..af07d014a 100644 --- a/app/Language/it/Error.php +++ b/app/Language/it/Error.php @@ -1,6 +1,14 @@ "Non hai l'autorizzazione per accedere al modulo denominato", - "unknown" => "Errore sconosciuto", + 'no_permission_module' => 'Non hai l\'autorizzazione per accedere al modulo denominato', + 'unable_to_create_env_file' => 'Impossibile creare {filePath} per configurare throttle.key', + 'unable_to_lock_file' => 'Impossibile bloccare {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Impossibile aprire {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Impossibile salvare throttle.key in {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Impossibile leggere {filePath} per configurare throttle.key', + 'unknown' => 'Errore sconosciuto', ]; diff --git a/app/Language/ka/Config.php b/app/Language/ka/Config.php index c72e701c9..bf874f7fe 100644 --- a/app/Language/ka/Config.php +++ b/app/Language/ka/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'sendmail-ის ბილიკი არასწორია. დაშვებულია მხოლოდ ასოები, ციფრები, დეფისები, ხაზგასმები, დახრილი ხაზები, უკუხაზები, ორწერტილები, ჰარეები და წერტილები.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => '', diff --git a/app/Language/km/Attributes.php b/app/Language/km/Attributes.php index 557e51522..41a6c78c5 100644 --- a/app/Language/km/Attributes.php +++ b/app/Language/km/Attributes.php @@ -1,34 +1,36 @@ "ព៌តមានបន្ថែម មិនអាចមានអក្សរ '_' រឺ '|'", - "confirm_delete" => "តើអ្នកពិតជាចង់លុប ព៌តមានបន្ថែម ដែលបានជ្រើសរើស?", - "confirm_restore" => "តើអ្នកពិតជាដាក់ឡើងវិញនៅ ព៌តមានបន្ថែម ដែលបានជ្រើសរើស?", - "definition_cannot_be_deleted" => "មិនអាចលុបព៌តមានបន្ថែមដែលបានជ្រើសរើស", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "ព៌តមានបន្ថែម {0} មិនអាចថែម រឺកែប្រែបានឡើយ។​ សូមចូលទៅឆែករបាយការណ៍កំហុស។", - "definition_flags" => "ដាក់បង្ហាញព៌តមានបន្ថែម", - "definition_group" => "ក្រុម", - "definition_id" => "លេខរៀង", - "definition_name" => "បន្ថែម ព៌តមាន", - "definition_name_required" => "ឈ្មោះនៃព៌តមានបន្ថែម​ គឺត្រូវការចាំបាច់ត្រូវបំពេញ", - "definition_one_or_multiple" => "ព៌តមានបន្ថែម", - "definition_successful_adding" => "", - "definition_successful_deleted" => "", - "definition_successful_updating" => "", - "definition_type" => "", - "definition_type_required" => "", - "definition_unit" => "", - "definition_values" => "", - "new" => "", - "no_attributes_to_display" => "", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => 'ព៌តមានបន្ថែម មិនអាចមានអក្សរ \'_\' រឺ \'|\'', + 'confirm_delete' => 'តើអ្នកពិតជាចង់លុប ព៌តមានបន្ថែម ដែលបានជ្រើសរើស?', + 'confirm_restore' => 'តើអ្នកពិតជាដាក់ឡើងវិញនៅ ព៌តមានបន្ថែម ដែលបានជ្រើសរើស?', + 'definition_cannot_be_deleted' => 'មិនអាចលុបព៌តមានបន្ថែមដែលបានជ្រើសរើស', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'ព៌តមានបន្ថែម {0} មិនអាចថែម រឺកែប្រែបានឡើយ។​ សូមចូលទៅឆែករបាយការណ៍កំហុស។', + 'definition_flags' => 'ដាក់បង្ហាញព៌តមានបន្ថែម', + 'definition_group' => 'ក្រុម', + 'definition_id' => 'លេខរៀង', + 'definition_name' => 'បន្ថែម ព៌តមាន', + 'definition_name_required' => 'ឈ្មោះនៃព៌តមានបន្ថែម​ គឺត្រូវការចាំបាច់ត្រូវបំពេញ', + 'definition_one_or_multiple' => 'ព៌តមានបន្ថែម', + 'definition_successful_adding' => '', + 'definition_successful_deleted' => '', + 'definition_successful_updating' => '', + 'definition_type' => '', + 'definition_type_required' => '', + 'definition_unit' => '', + 'definition_values' => '', + 'new' => '', + 'no_attributes_to_display' => '', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'បង្ហាញក្នុងការស្វែងរក', + 'show_in_search_visibility' => 'ស្វែងរក', + 'update' => '', ]; diff --git a/app/Language/km/Config.php b/app/Language/km/Config.php index a6fd6b044..7bdac6fda 100644 --- a/app/Language/km/Config.php +++ b/app/Language/km/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'ផ្លូវ sendmail មិនត្រឹមត្រូវ។ អនុញ្ញាតតែអក្សរ លេខ សញ្ញាដាច់បន្ទាត់ សញ្ញាគូសក្រោម សញ្ញាចែកមុខ សញ្ញាចែកក្រោយ សញ្ញាចំណុចពីរ ចន្លោះ និងចំណុចប៉ុណ្ណោះ។', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/km/Error.php b/app/Language/km/Error.php index 4842a3ac9..95500ad28 100644 --- a/app/Language/km/Error.php +++ b/app/Language/km/Error.php @@ -1,6 +1,14 @@ "អ្នកមិនមានសិទ្ធដើម្បីចូលទៅ ផ្នែកនោះទេ", - "unknown" => "កំហុសមិនស្គាល់", + 'no_permission_module' => 'អ្នកមិនមានសិទ្ធដើម្បីចូលទៅ ផ្នែកនោះទេ', + 'unable_to_create_env_file' => 'មិនអាចបង្កើត {filePath} ដើម្បីរៀបចំ throttle.key', + 'unable_to_lock_file' => 'មិនអាចចាក់សោ {filePath}: {reason}', + 'unable_to_open_lock_file' => 'មិនអាចបើក {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'មិនអាចរក្សាទុក throttle.key ទៅ {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'មិនអាចអាន {filePath} ដើម្បីរៀបចំ throttle.key', + 'unknown' => 'កំហុសមិនស្គាល់', ]; diff --git a/app/Language/km/Reports.php b/app/Language/km/Reports.php index eddf94a49..9d20f2d65 100644 --- a/app/Language/km/Reports.php +++ b/app/Language/km/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/lo/Attributes.php b/app/Language/lo/Attributes.php index e5bb66296..10bcfa392 100644 --- a/app/Language/lo/Attributes.php +++ b/app/Language/lo/Attributes.php @@ -1,34 +1,36 @@ "ຄ່າແອດທິບິວບໍ່ສາມາດມີ ':' ຫລື '|'", - "confirm_delete" => "ແນ່ໃຈຫຼືບໍທີ່ຈະລືບລາຍການທີ່ເລືອກ", - "confirm_restore" => "ແນ່ໃຈຫຼືບໍທີ່ຈະຄືນຄ່າແອັດທິບິ້ວດັ່ງກ່າວ?", - "definition_cannot_be_deleted" => "ບໍສາມາດລືບລາຍການທີ່ເລືອກໄດ້", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "ລາຍການ {0} ບໍສາມາດເພີ່ມ ຫຼື ແກ້ໄຂ. ກະລຸນາກວດສອບຢູ່ log ຂໍ້ຜິດຜາດ", - "definition_flags" => "ຄູນສົມບັດການເບິ່ງເຫັນ", - "definition_group" => "ກຸ່ມ", - "definition_id" => "ລະຫັດ", - "definition_name" => "ເພີ່ມລາຍການ", - "definition_name_required" => "ຕ້ອງໄດ້ລະບຸຊື່ລາຍການ", - "definition_one_or_multiple" => "ລາຍການ", - "definition_successful_adding" => "ເພີ່ມລາຍການສຳເລັດແລ້ວ", - "definition_successful_deleted" => "ລຶບລາຍການສຳເລັດແລ້ວ", - "definition_successful_updating" => "ແກ້ໄຂລາຍການສຳເລັດແລ້ວ", - "definition_type" => "ປະເພດລາຍການ", - "definition_type_required" => "ປະເພດແອດທີບິວ ຈຳເປັນຕ້ອງໃຊ້", - "definition_unit" => "", - "definition_values" => "", - "new" => "", - "no_attributes_to_display" => "", - "receipt_visibility" => "", - "show_in_items" => "", - "show_in_items_visibility" => "", - "show_in_receipt" => "", - "show_in_receivings" => "", - "show_in_receivings_visibility" => "", - "show_in_sales" => "", - "show_in_sales_visibility" => "", - "update" => "", + 'attribute_value_invalid_chars' => 'ຄ່າແອດທິບິວບໍ່ສາມາດມີ \':\' ຫລື \'|\'', + 'confirm_delete' => 'ແນ່ໃຈຫຼືບໍທີ່ຈະລືບລາຍການທີ່ເລືອກ', + 'confirm_restore' => 'ແນ່ໃຈຫຼືບໍທີ່ຈະຄືນຄ່າແອັດທິບິ້ວດັ່ງກ່າວ?', + 'definition_cannot_be_deleted' => 'ບໍສາມາດລືບລາຍການທີ່ເລືອກໄດ້', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'ລາຍການ {0} ບໍສາມາດເພີ່ມ ຫຼື ແກ້ໄຂ. ກະລຸນາກວດສອບຢູ່ log ຂໍ້ຜິດຜາດ', + 'definition_flags' => 'ຄູນສົມບັດການເບິ່ງເຫັນ', + 'definition_group' => 'ກຸ່ມ', + 'definition_id' => 'ລະຫັດ', + 'definition_name' => 'ເພີ່ມລາຍການ', + 'definition_name_required' => 'ຕ້ອງໄດ້ລະບຸຊື່ລາຍການ', + 'definition_one_or_multiple' => 'ລາຍການ', + 'definition_successful_adding' => 'ເພີ່ມລາຍການສຳເລັດແລ້ວ', + 'definition_successful_deleted' => 'ລຶບລາຍການສຳເລັດແລ້ວ', + 'definition_successful_updating' => 'ແກ້ໄຂລາຍການສຳເລັດແລ້ວ', + 'definition_type' => 'ປະເພດລາຍການ', + 'definition_type_required' => 'ປະເພດແອດທີບິວ ຈຳເປັນຕ້ອງໃຊ້', + 'definition_unit' => '', + 'definition_values' => '', + 'new' => '', + 'no_attributes_to_display' => '', + 'receipt_visibility' => '', + 'show_in_items' => '', + 'show_in_items_visibility' => '', + 'show_in_receipt' => '', + 'show_in_receivings' => '', + 'show_in_receivings_visibility' => '', + 'show_in_sales' => '', + 'show_in_sales_visibility' => '', + 'show_in_search' => 'ສະແດງໃນການຄົ້ນຫາ', + 'show_in_search_visibility' => 'ຄົ້ນຫາ', + 'update' => '', ]; diff --git a/app/Language/lo/Config.php b/app/Language/lo/Config.php index 34d3f48cb..72a09feb2 100644 --- a/app/Language/lo/Config.php +++ b/app/Language/lo/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'ເສັ້ນທາງ sendmail ບໍ່ຖືກຕ້ອງ. ອະນຸຍາດສະເພາະຕົວອັກສອນ, ຕົວເລກ, ຂີດກາງ, ຂີດກ້ອງ, ຂີດຂ້າງໜ້າ, ຂີດຂ້າງຫຼັງ, ຈໍ້າສອງເມັດ, ວັກ ແລະ ຈຸດເທົ່ານັ້ນ.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/lo/Error.php b/app/Language/lo/Error.php index 732c0ed3d..36dace41e 100644 --- a/app/Language/lo/Error.php +++ b/app/Language/lo/Error.php @@ -1,6 +1,14 @@ "ທ່ານບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ເຂົ້າເຖິງຂໍ້ມູນສ່ວນນີ້", - "unknown" => "ບໍ່ຮູ້", + 'no_permission_module' => 'ທ່ານບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ເຂົ້າເຖິງຂໍ້ມູນສ່ວນນີ້', + 'unable_to_create_env_file' => 'ບໍ່ສາມາດສ້າງ {filePath} ເພື່ອກຽມ throttle.key ໄດ້', + 'unable_to_lock_file' => 'ບໍ່ສາມາດລັອກ {filePath}: {reason}', + 'unable_to_open_lock_file' => 'ບໍ່ສາມາດເປີດ {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'ບໍ່ສາມາດບັນທຶກ throttle.key ໄປທີ່ {filePath} ໄດ້', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'ບໍ່ສາມາດອ່ານ {filePath} ເພື່ອກຽມ throttle.key ໄດ້', + 'unknown' => 'ບໍ່ຮູ້', ]; diff --git a/app/Language/lo/Reports.php b/app/Language/lo/Reports.php index fa31c61b5..581bb197d 100644 --- a/app/Language/lo/Reports.php +++ b/app/Language/lo/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "Taxes", "taxes_summary_report" => "Taxes Summary Report", + "toggle_cost_and_profit" => "", "total" => "Total", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "", diff --git a/app/Language/ml/Attributes.php b/app/Language/ml/Attributes.php index 82911ea55..37340d7b2 100644 --- a/app/Language/ml/Attributes.php +++ b/app/Language/ml/Attributes.php @@ -1,34 +1,36 @@ "Attribute value cannot contain ':' or '|'", - "confirm_delete" => "Are you sure you want to delete the selected attribute(s)?", - "confirm_restore" => "", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Attribute Name", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'Attribute value cannot contain \':\' or \'|\'', + 'confirm_delete' => 'Are you sure you want to restore the selected attribute(s)?', + 'confirm_restore' => 'Are you sure you want to delete the selected attribute(s)?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute type is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated Item Kit', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute name is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Items to display.', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'തിരയലിൽ കാണിക്കുക', + 'show_in_search_visibility' => 'തിരയൽ', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/ml/Config.php b/app/Language/ml/Config.php index bb9794fb2..d6901e6a8 100644 --- a/app/Language/ml/Config.php +++ b/app/Language/ml/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'അസാധുവായ sendmail പാത്ത്. അക്ഷരങ്ങൾ, അക്കങ്ങൾ, ഡാഷുകൾ, അടിവരകൾ, സ്ലാഷുകൾ, ബാക്ക്സ്ലാഷുകൾ, കോളനുകൾ, സ്പേസുകൾ, ഡോട്ടുകൾ എന്നിവ മാത്രമേ അനുവദനീയമായുള്ളൂ.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/ml/Error.php b/app/Language/ml/Error.php index 64ebe1e41..175f49ebf 100644 --- a/app/Language/ml/Error.php +++ b/app/Language/ml/Error.php @@ -1,6 +1,14 @@ "", - "unknown" => "", + 'no_permission_module' => 'ഈ പേരിലുള്ള മൊഡ്യൂൾ ആക്‌സസ് ചെയ്യാൻ നിങ്ങൾക്ക് അനുമതിയില്ല', + 'unable_to_create_env_file' => 'throttle.key സജ്ജീകരിക്കാൻ {filePath} സൃഷ്‌ടിക്കാൻ കഴിഞ്ഞില്ല', + 'unable_to_lock_file' => '{filePath} ലോക്ക് ചെയ്യാൻ കഴിഞ്ഞില്ല: {reason}', + 'unable_to_open_lock_file' => '{filePath} തുറക്കാൻ കഴിഞ്ഞില്ല: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key {filePath}-ൽ സൂക്ഷിക്കാൻ കഴിഞ്ഞില്ല', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'throttle.key സജ്ജീകരിക്കാൻ {filePath} വായിക്കാൻ കഴിഞ്ഞില്ല', + 'unknown' => 'അപ്രതീക്ഷിത പിശക്', ]; diff --git a/app/Language/ml/Reports.php b/app/Language/ml/Reports.php index 93316dfd2..15ff6bed9 100644 --- a/app/Language/ml/Reports.php +++ b/app/Language/ml/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/nb/Attributes.php b/app/Language/nb/Attributes.php index 377b84f4b..d67266cb5 100644 --- a/app/Language/nb/Attributes.php +++ b/app/Language/nb/Attributes.php @@ -1,34 +1,36 @@ "Attribute value cannot contain ':' or '|'", - "confirm_delete" => "Are you sure you want to delete the selected attribute(s)?", - "confirm_restore" => "Are you sure you want to restore the selected attribute(s)?", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'Attribute value cannot contain \':\' or \'|\'', + 'confirm_delete' => 'Are you sure you want to delete the selected attribute(s)?', + 'confirm_restore' => 'Are you sure you want to restore the selected attribute(s)?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute name is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated attribute', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute type is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Items to display', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'Vis i søk', + 'show_in_search_visibility' => 'Søk', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/nb/Config.php b/app/Language/nb/Config.php index 19fa62813..0a2289089 100644 --- a/app/Language/nb/Config.php +++ b/app/Language/nb/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Ugyldig sendmail-sti. Kun bokstaver, tall, bindestreker, understreker, skråstreker, omvendte skråstreker, kolon, mellomrom og punktum er tillatt.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => '', diff --git a/app/Language/nb/Error.php b/app/Language/nb/Error.php index 64ebe1e41..5d66de671 100644 --- a/app/Language/nb/Error.php +++ b/app/Language/nb/Error.php @@ -1,6 +1,14 @@ "", - "unknown" => "", + 'no_permission_module' => 'Du har ikke tillatelse til å få tilgang til modulen som heter', + 'unable_to_create_env_file' => 'Kan ikke opprette {filePath} for å klargjøre throttle.key', + 'unable_to_lock_file' => 'Kan ikke låse {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Kan ikke åpne {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Kan ikke lagre throttle.key til {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Kan ikke lese {filePath} for å klargjøre throttle.key', + 'unknown' => 'Uventet feil', ]; diff --git a/app/Language/nb/Reports.php b/app/Language/nb/Reports.php index 93316dfd2..15ff6bed9 100644 --- a/app/Language/nb/Reports.php +++ b/app/Language/nb/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/nl-BE/Attributes.php b/app/Language/nl-BE/Attributes.php index 4d544a18d..409a29b36 100644 --- a/app/Language/nl-BE/Attributes.php +++ b/app/Language/nl-BE/Attributes.php @@ -1,34 +1,36 @@ "Attribuut waarde kan geen ':' of '|' bevatten", - "confirm_delete" => "Bent u zeker dat u de geselecteerde attributen wil verwijderen?", - "confirm_restore" => "Bent u zeker dat u de geselecteerde attributen wil herstellen?", - "definition_cannot_be_deleted" => "De geselecteerde attributen konden niet verwijderd worden", - "definition_invalid_group" => "De geselecteerde groep bestaat niet of is ongeldig.", - "definition_error_adding_updating" => "Attribuut {0} kon niet toegevoegd of gewijzigd worden. Kijk de error logs na.", - "definition_flags" => "Zichtbaarheid", - "definition_group" => "Groep", - "definition_id" => "Id", - "definition_name" => "Nieuw attribuut", - "definition_name_required" => "Attribuut naam is een verplicht veld", - "definition_one_or_multiple" => "Attribu(u)t(en)", - "definition_successful_adding" => "Attribuut succesvol toegevoegd", - "definition_successful_deleted" => "Attribu(u)t(en) succesvol verwijderd", - "definition_successful_updating" => "Wijzigingen bewaard voor", - "definition_type" => "Type", - "definition_type_required" => "Attribuut type is een verplicht veld", - "definition_unit" => "Meeteenheid", - "definition_values" => "Attribuut waardes", - "new" => "Nieuw Attribuut", - "no_attributes_to_display" => "Er werden geen attributen gevonden", - "receipt_visibility" => "Verkoop", - "show_in_items" => "Toon in producten", - "show_in_items_visibility" => "Producten", - "show_in_receipt" => "Toon in verkoop", - "show_in_receivings" => "Toon in orders", - "show_in_receivings_visibility" => "Orders", - "show_in_sales" => "Toon in verkoop", - "show_in_sales_visibility" => "Verkoop", - "update" => "Wijzig Attribuut", + 'attribute_value_invalid_chars' => 'Attribuut waarde kan geen \':\' of \'|\' bevatten', + 'confirm_delete' => 'Bent u zeker dat u de geselecteerde attributen wil verwijderen?', + 'confirm_restore' => 'Bent u zeker dat u de geselecteerde attributen wil herstellen?', + 'definition_cannot_be_deleted' => 'De geselecteerde attributen konden niet verwijderd worden', + 'definition_invalid_group' => 'De geselecteerde groep bestaat niet of is ongeldig.', + 'definition_error_adding_updating' => 'Attribuut {0} kon niet toegevoegd of gewijzigd worden. Kijk de error logs na.', + 'definition_flags' => 'Zichtbaarheid', + 'definition_group' => 'Groep', + 'definition_id' => 'Id', + 'definition_name' => 'Nieuw attribuut', + 'definition_name_required' => 'Attribuut naam is een verplicht veld', + 'definition_one_or_multiple' => 'Attribu(u)t(en)', + 'definition_successful_adding' => 'Attribuut succesvol toegevoegd', + 'definition_successful_deleted' => 'Attribu(u)t(en) succesvol verwijderd', + 'definition_successful_updating' => 'Wijzigingen bewaard voor', + 'definition_type' => 'Type', + 'definition_type_required' => 'Attribuut type is een verplicht veld', + 'definition_unit' => 'Meeteenheid', + 'definition_values' => 'Attribuut waardes', + 'new' => 'Nieuw Attribuut', + 'no_attributes_to_display' => 'Er werden geen attributen gevonden', + 'receipt_visibility' => 'Verkoop', + 'show_in_items' => 'Toon in producten', + 'show_in_items_visibility' => 'Producten', + 'show_in_receipt' => 'Toon in verkoop', + 'show_in_receivings' => 'Toon in orders', + 'show_in_receivings_visibility' => 'Orders', + 'show_in_sales' => 'Toon in verkoop', + 'show_in_sales_visibility' => 'Verkoop', + 'show_in_search' => 'Toon in zoeken', + 'show_in_search_visibility' => 'Zoeken', + 'update' => 'Wijzig Attribuut', ]; diff --git a/app/Language/nl-BE/Config.php b/app/Language/nl-BE/Config.php index 294577a96..b7432069a 100644 --- a/app/Language/nl-BE/Config.php +++ b/app/Language/nl-BE/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Opgeslagen SMS-bericht', 'msg_msg_placeholder' => 'Wilt u gebruik maken van een SMS-sjabloon? sla hier uw bericht op. Laat ander het vak leeg.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Wachtwoord is een verplicht veld', 'msg_src' => 'SMS-API Verzender ID', 'msg_src_required' => 'SMS-API Verzender ID is een verplicht veld', 'msg_uid' => 'SMS-API Gebruikersnaam', @@ -285,7 +284,7 @@ return [ 'right' => 'Rechts', 'sales_invoice_format' => 'Formattering Aankoop #', 'sales_quote_format' => 'Offerte formaat', - 'mailpath_invalid' => 'Ongeldig sendmail pad. Alleen letters, cijfers, strepen, underscores, slashes en punten zijn toegestaan.', + 'mailpath_invalid' => 'Ongeldig sendmail-pad. Enkel letters, cijfers, streepjes, onderstrepingstekens, schuine strepen, omgekeerde schuine strepen, dubbele punten, spaties en punten zijn toegelaten.', 'saved_successfully' => 'Configuratie werd bewaard.', 'saved_unsuccessfully' => 'Configuratie kon niet worden bewaard.', 'security_issue' => 'Waarschuwing voor Veiligheidslek', diff --git a/app/Language/nl-BE/Error.php b/app/Language/nl-BE/Error.php index 48e0926a7..a10a22121 100644 --- a/app/Language/nl-BE/Error.php +++ b/app/Language/nl-BE/Error.php @@ -1,6 +1,14 @@ "U hebt geen toegang tot de module genaamd", - "unknown" => "onbekend", + 'no_permission_module' => 'U hebt geen toegang tot de module genaamd', + 'unable_to_create_env_file' => 'Kan {filePath} niet aanmaken om throttle.key te voorzien', + 'unable_to_lock_file' => 'Kan {filePath} niet vergrendelen: {reason}', + 'unable_to_open_lock_file' => 'Kan {filePath} niet openen: {reason}', + 'unable_to_persist_throttle_key' => 'Kan throttle.key niet opslaan naar {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Kan {filePath} niet lezen om throttle.key te voorzien', + 'unknown' => 'onbekend', ]; diff --git a/app/Language/nl-BE/Reports.php b/app/Language/nl-BE/Reports.php index 0c4270f15..c0ce3f8a5 100644 --- a/app/Language/nl-BE/Reports.php +++ b/app/Language/nl-BE/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "VAT %", "taxes" => "Belastingen", "taxes_summary_report" => "Rapport Overzicht Belastingen", + "toggle_cost_and_profit" => "Kosten en winst wisselen", "total" => "Totaal", "total_inventory_value" => "Totale waarde stock", "total_low_sell_quantity" => "Totale Lage Verkoophoeveelheid", diff --git a/app/Language/nl-NL/Attributes.php b/app/Language/nl-NL/Attributes.php index 25acecf50..207fc4ac8 100644 --- a/app/Language/nl-NL/Attributes.php +++ b/app/Language/nl-NL/Attributes.php @@ -1,34 +1,36 @@ "Kenmerkwaarde mag niet '_' of '|' bevatten", - "confirm_delete" => "Weet u zeker dat u de geselecteerde kenmerken wilt verwijderen?", - "confirm_restore" => "Weet u zeker dat u de geselecteerde kenmerken wilt herstellen?", - "definition_cannot_be_deleted" => "Kan geselecteerde kenmerk(en) niet verwijderen", - "definition_invalid_group" => "De geselecteerde groep bestaat niet of is ongeldig.", - "definition_error_adding_updating" => "Kenmerk {0} kan niet worden toegevoegd of bijgewerkt. Bekijk het foutenlogboek.", - "definition_flags" => "Kenmerk zichtbaarheid", - "definition_group" => "Groep", - "definition_id" => "Id", - "definition_name" => "Kenmerk toevoegen", - "definition_name_required" => "Kenmerknaam is een vereist veld", - "definition_one_or_multiple" => "kenmerk(en)", - "definition_successful_adding" => "U heeft een artikel toegevoegd", - "definition_successful_deleted" => "U heeft verwijderd", - "definition_successful_updating" => "U heeft het kenmerk bijgewerkt", - "definition_type" => "Kenmerk soort", - "definition_type_required" => "Kenmerk soort is een vereist veld", - "definition_unit" => "Maateenheid", - "definition_values" => "Kenmerkwaarden", - "new" => "Nieuw kenmerk", - "no_attributes_to_display" => "Geen artikelen om te weergeven", - "receipt_visibility" => "Kassabon", - "show_in_items" => "Weergeven in artikelen", - "show_in_items_visibility" => "Artikelen", - "show_in_receipt" => "Weergeven op kassabon", - "show_in_receivings" => "Weergeven in leveringen", - "show_in_receivings_visibility" => "Leveringen", - "show_in_sales" => "Weergeven in verkopen", - "show_in_sales_visibility" => "Verkopen", - "update" => "Kenmerk bijwerken", + 'attribute_value_invalid_chars' => 'Kenmerkwaarde mag niet \'_\' of \'|\' bevatten', + 'confirm_delete' => 'Weet u zeker dat u de geselecteerde kenmerken wilt verwijderen?', + 'confirm_restore' => 'Weet u zeker dat u de geselecteerde kenmerken wilt herstellen?', + 'definition_cannot_be_deleted' => 'Kan geselecteerde kenmerk(en) niet verwijderen', + 'definition_invalid_group' => 'De geselecteerde groep bestaat niet of is ongeldig.', + 'definition_error_adding_updating' => 'Kenmerk {0} kan niet worden toegevoegd of bijgewerkt. Bekijk het foutenlogboek.', + 'definition_flags' => 'Kenmerk zichtbaarheid', + 'definition_group' => 'Groep', + 'definition_id' => 'Id', + 'definition_name' => 'Kenmerk toevoegen', + 'definition_name_required' => 'Kenmerknaam is een vereist veld', + 'definition_one_or_multiple' => 'kenmerk(en)', + 'definition_successful_adding' => 'U heeft een artikel toegevoegd', + 'definition_successful_deleted' => 'U heeft verwijderd', + 'definition_successful_updating' => 'U heeft het kenmerk bijgewerkt', + 'definition_type' => 'Kenmerk soort', + 'definition_type_required' => 'Kenmerk soort is een vereist veld', + 'definition_unit' => 'Maateenheid', + 'definition_values' => 'Kenmerkwaarden', + 'new' => 'Nieuw kenmerk', + 'no_attributes_to_display' => 'Geen artikelen om te weergeven', + 'receipt_visibility' => 'Kassabon', + 'show_in_items' => 'Weergeven in artikelen', + 'show_in_items_visibility' => 'Artikelen', + 'show_in_receipt' => 'Weergeven op kassabon', + 'show_in_receivings' => 'Weergeven in leveringen', + 'show_in_receivings_visibility' => 'Leveringen', + 'show_in_sales' => 'Weergeven in verkopen', + 'show_in_sales_visibility' => 'Verkopen', + 'show_in_search' => 'Weergeven in zoekopdrachten', + 'show_in_search_visibility' => 'Zoeken', + 'update' => 'Kenmerk bijwerken', ]; diff --git a/app/Language/nl-NL/Config.php b/app/Language/nl-NL/Config.php index b73e50b90..4234d84a6 100644 --- a/app/Language/nl-NL/Config.php +++ b/app/Language/nl-NL/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Opgeslagen sms-bericht', 'msg_msg_placeholder' => 'Sla uw bericht hier op als u een sms-sjabloon wilt maken, zo niet laat het vak leeg.', 'msg_pwd' => 'Sms API wachtwoord', - 'msg_pwd_required' => 'Sms API wachtwoord is een vereist veld', 'msg_src' => 'Sms API afzender ID', 'msg_src_required' => 'Sms API afzender ID is een vereist veld', 'msg_uid' => 'Sms API gebruikersnaam', @@ -285,7 +284,7 @@ return [ 'right' => 'Rechts', 'sales_invoice_format' => 'Indeling verkoopfactuur', 'sales_quote_format' => 'Indeling verkoopofferte', - 'mailpath_invalid' => 'Ongeldig sendmail pad. Alleen letters, cijfers, strepen, underscores, slashes en punten zijn toegestaan.', + 'mailpath_invalid' => 'Ongeldig sendmail-pad. Alleen letters, cijfers, streepjes, onderstrepingstekens, schuine strepen, omgekeerde schuine strepen, dubbele punten, spaties en punten zijn toegestaan.', 'saved_successfully' => 'Configuratie opgeslagen.', 'saved_unsuccessfully' => 'Configuratie opslaan mislukt.', 'security_issue' => 'Beveilingskwetsbaarheid waarschuwing', diff --git a/app/Language/nl-NL/Error.php b/app/Language/nl-NL/Error.php index a6dc506e5..46b1009dc 100644 --- a/app/Language/nl-NL/Error.php +++ b/app/Language/nl-NL/Error.php @@ -1,6 +1,14 @@ "U bent niet gemachtigd voor toegang tot de module genaamd", - "unknown" => "Onverwachte fout", + 'no_permission_module' => 'U bent niet gemachtigd voor toegang tot de module genaamd', + 'unable_to_create_env_file' => 'Kan {filePath} niet aanmaken om throttle.key te voorzien', + 'unable_to_lock_file' => 'Kan {filePath} niet vergrendelen: {reason}', + 'unable_to_open_lock_file' => 'Kan {filePath} niet openen: {reason}', + 'unable_to_persist_throttle_key' => 'Kan throttle.key niet opslaan naar {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Kan {filePath} niet lezen om throttle.key te voorzien', + 'unknown' => 'Onverwachte fout', ]; diff --git a/app/Language/pl/Attributes.php b/app/Language/pl/Attributes.php index 3edda3db5..cf184f01d 100644 --- a/app/Language/pl/Attributes.php +++ b/app/Language/pl/Attributes.php @@ -1,34 +1,36 @@ "Wartość atrybutu nie może zawierać'_' lub '|'", - "confirm_delete" => "Czy jesteś pewny, że chcesz usunąć wybrane atrybuty?", - "confirm_restore" => "Czy jesteś pewien, że chcesz przywrócić zaznaczone atrybuty?", - "definition_cannot_be_deleted" => "Nie można usunąć wybranych atrybutów", - "definition_invalid_group" => "Wybrana grupa nie istnieje lub jest nieprawidłowa.", - "definition_error_adding_updating" => "Atrybut 51 nie może zostać dodany lub zaktualizowany. Sprawdź dziennik błędów.", - "definition_flags" => "Widoczność atrybutu", - "definition_group" => "Grupa", - "definition_id" => "Id", - "definition_name" => "Dodaj atrybut", - "definition_name_required" => "Nazwa atrybutu jest wymagana", - "definition_one_or_multiple" => "atrybut(y)", - "definition_successful_adding" => "Pomyślnie dodano element", - "definition_successful_deleted" => "Pomyślnie usunięto", - "definition_successful_updating" => "Pomyślnie zaktualizowano atrybut", - "definition_type" => "Typ atrybutu", - "definition_type_required" => "Typ atrybutu jest wymagany", - "definition_unit" => "Jednostka miary", - "definition_values" => "Wartość atrybutu", - "new" => "Nowy atrybut", - "no_attributes_to_display" => "Brak elementów do wyświetlenia", - "receipt_visibility" => "Paragon", - "show_in_items" => "Pokaż w produktach", - "show_in_items_visibility" => "Produkty", - "show_in_receipt" => "Pokaż w paragonie", - "show_in_receivings" => "Pokaż w dostawach", - "show_in_receivings_visibility" => "Dostawy", - "show_in_sales" => "Pokaż w sprzedażach", - "show_in_sales_visibility" => "Sprzedaże", - "update" => "Zaktualizuj atrybut", + 'attribute_value_invalid_chars' => 'Wartość atrybutu nie może zawierać\'_\' lub \'|\'', + 'confirm_delete' => 'Czy jesteś pewny, że chcesz usunąć wybrane atrybuty?', + 'confirm_restore' => 'Czy jesteś pewien, że chcesz przywrócić zaznaczone atrybuty?', + 'definition_cannot_be_deleted' => 'Nie można usunąć wybranych atrybutów', + 'definition_invalid_group' => 'Wybrana grupa nie istnieje lub jest nieprawidłowa.', + 'definition_error_adding_updating' => 'Atrybut 51 nie może zostać dodany lub zaktualizowany. Sprawdź dziennik błędów.', + 'definition_flags' => 'Widoczność atrybutu', + 'definition_group' => 'Grupa', + 'definition_id' => 'Id', + 'definition_name' => 'Dodaj atrybut', + 'definition_name_required' => 'Nazwa atrybutu jest wymagana', + 'definition_one_or_multiple' => 'atrybut(y)', + 'definition_successful_adding' => 'Pomyślnie dodano element', + 'definition_successful_deleted' => 'Pomyślnie usunięto', + 'definition_successful_updating' => 'Pomyślnie zaktualizowano atrybut', + 'definition_type' => 'Typ atrybutu', + 'definition_type_required' => 'Typ atrybutu jest wymagany', + 'definition_unit' => 'Jednostka miary', + 'definition_values' => 'Wartość atrybutu', + 'new' => 'Nowy atrybut', + 'no_attributes_to_display' => 'Brak elementów do wyświetlenia', + 'receipt_visibility' => 'Paragon', + 'show_in_items' => 'Pokaż w produktach', + 'show_in_items_visibility' => 'Produkty', + 'show_in_receipt' => 'Pokaż w paragonie', + 'show_in_receivings' => 'Pokaż w dostawach', + 'show_in_receivings_visibility' => 'Dostawy', + 'show_in_sales' => 'Pokaż w sprzedażach', + 'show_in_sales_visibility' => 'Sprzedaże', + 'show_in_search' => 'Pokaż w wyszukiwaniu', + 'show_in_search_visibility' => 'Wyszukiwanie', + 'update' => 'Zaktualizuj atrybut', ]; diff --git a/app/Language/pl/Config.php b/app/Language/pl/Config.php index 8aed59197..a156817d2 100644 --- a/app/Language/pl/Config.php +++ b/app/Language/pl/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,6 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', + 'mailpath_invalid' => 'Nieprawidłowa ścieżka sendmail. Dozwolone są tylko litery, cyfry, myślniki, podkreślenia, ukośniki, ukośniki odwrotne, dwukropki, spacje i kropki.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/pl/Error.php b/app/Language/pl/Error.php index 35a6e713e..33f5e0d78 100644 --- a/app/Language/pl/Error.php +++ b/app/Language/pl/Error.php @@ -1,6 +1,14 @@ "Nie masz dostępu do modułu", - "unknown" => "Niespodziewany błąd", + 'no_permission_module' => 'Nie masz dostępu do modułu', + 'unable_to_create_env_file' => 'Nie można utworzyć {filePath}, aby przygotować throttle.key', + 'unable_to_lock_file' => 'Nie można zablokować {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Nie można otworzyć {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Nie można zapisać throttle.key w {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Nie można odczytać {filePath}, aby przygotować throttle.key', + 'unknown' => 'Niespodziewany błąd', ]; diff --git a/app/Language/pl/Reports.php b/app/Language/pl/Reports.php index d3aae242b..b1b4644d6 100644 --- a/app/Language/pl/Reports.php +++ b/app/Language/pl/Reports.php @@ -128,6 +128,7 @@ return [ 'tax_rate' => "", 'taxes' => "", 'taxes_summary_report' => "", + 'toggle_cost_and_profit' => "", 'total' => "", 'total_inventory_value' => "", 'total_low_sell_quantity' => "", diff --git a/app/Language/pt-BR/Attributes.php b/app/Language/pt-BR/Attributes.php index f71960da8..a69b5031f 100644 --- a/app/Language/pt-BR/Attributes.php +++ b/app/Language/pt-BR/Attributes.php @@ -1,34 +1,36 @@ "Valor do atributo não pode conter ':' ou '|'", - "confirm_delete" => "Tem certeza de que deseja excluir os atributos selecionados?", - "confirm_restore" => "Tem certeza de que deseja restaurar o(s) atributo(s) selecionado(s)?", - "definition_cannot_be_deleted" => "Não foi possível excluir atributo selecionado (s)", - "definition_invalid_group" => "O grupo selecionado não existe ou é inválido.", - "definition_error_adding_updating" => "Atributo {0} não pode ser adicionado ou atualizado. Por favor verifique o log de erros.", - "definition_flags" => "Visibilidade de atributo", - "definition_group" => "Grupo", - "definition_id" => "Id", - "definition_name" => "Adicionar Atributo", - "definition_name_required" => "Nome do atributo é um campo obrigatório", - "definition_one_or_multiple" => "Atributo(s)", - "definition_successful_adding" => "Você adicionou com êxito o item", - "definition_successful_deleted" => "Você excluiu com êxito", - "definition_successful_updating" => "Você atualizou com êxito o atributo", - "definition_type" => "Tipo de atributo", - "definition_type_required" => "Tipo de atributo é um campo obrigatório", - "definition_unit" => "Unidade de medida", - "definition_values" => "Valores de atributo", - "new" => "Novo Atributo", - "no_attributes_to_display" => "Não há itens para exibir", - "receipt_visibility" => "Recibo", - "show_in_items" => "Mostrar em itens", - "show_in_items_visibility" => "Itens", - "show_in_receipt" => "Mostrar no recibo", - "show_in_receivings" => "Mostrar em recebimentos", - "show_in_receivings_visibility" => "Recebimentos", - "show_in_sales" => "Mostrar em vendas", - "show_in_sales_visibility" => "Vendas", - "update" => "Atualizar atributo", + 'attribute_value_invalid_chars' => 'Valor do atributo não pode conter \':\' ou \'|\'', + 'confirm_delete' => 'Tem certeza de que deseja excluir os atributos selecionados?', + 'confirm_restore' => 'Tem certeza de que deseja restaurar o(s) atributo(s) selecionado(s)?', + 'definition_cannot_be_deleted' => 'Não foi possível excluir atributo selecionado (s)', + 'definition_invalid_group' => 'O grupo selecionado não existe ou é inválido.', + 'definition_error_adding_updating' => 'Atributo {0} não pode ser adicionado ou atualizado. Por favor verifique o log de erros.', + 'definition_flags' => 'Visibilidade de atributo', + 'definition_group' => 'Grupo', + 'definition_id' => 'Id', + 'definition_name' => 'Adicionar Atributo', + 'definition_name_required' => 'Nome do atributo é um campo obrigatório', + 'definition_one_or_multiple' => 'Atributo(s)', + 'definition_successful_adding' => 'Você adicionou com êxito o item', + 'definition_successful_deleted' => 'Você excluiu com êxito', + 'definition_successful_updating' => 'Você atualizou com êxito o atributo', + 'definition_type' => 'Tipo de atributo', + 'definition_type_required' => 'Tipo de atributo é um campo obrigatório', + 'definition_unit' => 'Unidade de medida', + 'definition_values' => 'Valores de atributo', + 'new' => 'Novo Atributo', + 'no_attributes_to_display' => 'Não há itens para exibir', + 'receipt_visibility' => 'Recibo', + 'show_in_items' => 'Mostrar em itens', + 'show_in_items_visibility' => 'Itens', + 'show_in_receipt' => 'Mostrar no recibo', + 'show_in_receivings' => 'Mostrar em recebimentos', + 'show_in_receivings_visibility' => 'Recebimentos', + 'show_in_sales' => 'Mostrar em vendas', + 'show_in_sales_visibility' => 'Vendas', + 'show_in_search' => 'Mostrar na busca', + 'show_in_search_visibility' => 'Busca', + 'update' => 'Atualizar atributo', ]; diff --git a/app/Language/pt-BR/Config.php b/app/Language/pt-BR/Config.php index b21b311ee..079794b85 100644 --- a/app/Language/pt-BR/Config.php +++ b/app/Language/pt-BR/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Salvar mensagem de texto', 'msg_msg_placeholder' => 'Se você deseja usar um modelo de SMS salvar a sua mensagem aqui. Caso contrário, deixe a caixa em branco.', 'msg_pwd' => 'SMS-API senha', - 'msg_pwd_required' => 'SMS-API Senha é um campo obrigatório', 'msg_src' => 'SMS-API Remetente ID', 'msg_src_required' => 'SMS-API Remetente ID é um campo obrigatório', 'msg_uid' => 'SMS-API usuário', @@ -285,7 +284,7 @@ return [ 'right' => 'Direita', 'sales_invoice_format' => 'Formato da Fatura de Vendas', 'sales_quote_format' => 'Formato de cotação de vendas', - 'mailpath_invalid' => 'Caminho do sendmail inválido. Apenas letras, números, traços, sublinhados, barras e pontos são permitidos.', + 'mailpath_invalid' => 'Caminho do sendmail inválido. Apenas letras, números, traços, sublinhados, barras, barras invertidas, dois-pontos, espaços e pontos são permitidos.', 'saved_successfully' => 'Configuração salva com sucesso.', 'saved_unsuccessfully' => 'Configuração não salva.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/pt-BR/Error.php b/app/Language/pt-BR/Error.php index d63d1da4d..b524104dd 100644 --- a/app/Language/pt-BR/Error.php +++ b/app/Language/pt-BR/Error.php @@ -1,6 +1,14 @@ "Você não tem permissão para acessar o módulo chamado", - "unknown" => "Desconhecido", + 'no_permission_module' => 'Você não tem permissão para acessar o módulo chamado', + 'unable_to_create_env_file' => 'Não foi possível criar {filePath} para provisionar throttle.key', + 'unable_to_lock_file' => 'Não foi possível bloquear {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Não foi possível abrir {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Não foi possível persistir throttle.key em {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Não foi possível ler {filePath} para provisionar throttle.key', + 'unknown' => 'Desconhecido', ]; diff --git a/app/Language/ro/Attributes.php b/app/Language/ro/Attributes.php index 921b86361..7032d6bb2 100644 --- a/app/Language/ro/Attributes.php +++ b/app/Language/ro/Attributes.php @@ -1,34 +1,36 @@ "Valoare atribut nu poate contine ':' sau '|'", - "confirm_delete" => "Sigur doriti stergerea atributului/atributelor selectat(e)?", - "confirm_restore" => "", - "definition_cannot_be_deleted" => "Nu se poate sterge atributul/atributele selectat(e)", - "definition_invalid_group" => "Grupul selectat nu există sau este invalid.", - "definition_error_adding_updating" => "", - "definition_flags" => "Vizibilitate atribut", - "definition_group" => "Grup", - "definition_id" => "Id", - "definition_name" => "Adauga atribut", - "definition_name_required" => "Nume atribut este un camp obligatoriu", - "definition_one_or_multiple" => "atribut(e)", - "definition_successful_adding" => "Ati adaugat articolul cu succes", - "definition_successful_deleted" => "Ati sters cu succes", - "definition_successful_updating" => "Ati actualizat atributul cu succes", - "definition_type" => "Tip Atribut", - "definition_type_required" => "Tip Atribut este camp obligatoriu", - "definition_unit" => "", - "definition_values" => "Valori Atribut", - "new" => "Atribut nou", - "no_attributes_to_display" => "Nu sunt articole de afisat", - "receipt_visibility" => "Chitanta", - "show_in_items" => "Arata in articole", - "show_in_items_visibility" => "Articole", - "show_in_receipt" => "Arata in chitanta", - "show_in_receivings" => "Arata in receptii", - "show_in_receivings_visibility" => "Receptii", - "show_in_sales" => "Arata in vanzari", - "show_in_sales_visibility" => "Vanzari", - "update" => "Actualizare Atribut", + 'attribute_value_invalid_chars' => 'Valoare atribut nu poate contine \':\' sau \'|\'', + 'confirm_delete' => 'Sigur doriti stergerea atributului/atributelor selectat(e)?', + 'confirm_restore' => '', + 'definition_cannot_be_deleted' => 'Nu se poate sterge atributul/atributele selectat(e)', + 'definition_invalid_group' => 'Grupul selectat nu există sau este invalid.', + 'definition_error_adding_updating' => '', + 'definition_flags' => 'Vizibilitate atribut', + 'definition_group' => 'Grup', + 'definition_id' => 'Id', + 'definition_name' => 'Adauga atribut', + 'definition_name_required' => 'Nume atribut este un camp obligatoriu', + 'definition_one_or_multiple' => 'atribut(e)', + 'definition_successful_adding' => 'Ati adaugat articolul cu succes', + 'definition_successful_deleted' => 'Ati sters cu succes', + 'definition_successful_updating' => 'Ati actualizat atributul cu succes', + 'definition_type' => 'Tip Atribut', + 'definition_type_required' => 'Tip Atribut este camp obligatoriu', + 'definition_unit' => '', + 'definition_values' => 'Valori Atribut', + 'new' => 'Atribut nou', + 'no_attributes_to_display' => 'Nu sunt articole de afisat', + 'receipt_visibility' => 'Chitanta', + 'show_in_items' => 'Arata in articole', + 'show_in_items_visibility' => 'Articole', + 'show_in_receipt' => 'Arata in chitanta', + 'show_in_receivings' => 'Arata in receptii', + 'show_in_receivings_visibility' => 'Receptii', + 'show_in_sales' => 'Arata in vanzari', + 'show_in_sales_visibility' => 'Vanzari', + 'show_in_search' => 'Arata in cautare', + 'show_in_search_visibility' => 'Cautare', + 'update' => 'Actualizare Atribut', ]; diff --git a/app/Language/ro/Config.php b/app/Language/ro/Config.php index f86518373..c6cd733d0 100644 --- a/app/Language/ro/Config.php +++ b/app/Language/ro/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Cale sendmail invalidă. Sunt permise doar litere, cifre, liniuțe, liniuțe de subliniere, bare oblice, bare oblice inverse, două puncte, spații și puncte.', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/ro/Error.php b/app/Language/ro/Error.php index b81903d5a..d1b54166b 100644 --- a/app/Language/ro/Error.php +++ b/app/Language/ro/Error.php @@ -1,6 +1,14 @@ "Nu aveti permisiunea de acces a modulului numit", - "unknown" => "Eroare neasteptata", + 'no_permission_module' => 'Nu aveti permisiunea de acces a modulului numit', + 'unable_to_create_env_file' => 'Nu s-a putut crea {filePath} pentru a aproviziona throttle.key', + 'unable_to_lock_file' => 'Nu s-a putut bloca {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Nu s-a putut deschide {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Nu s-a putut persista throttle.key in {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Nu s-a putut citi {filePath} pentru a aproviziona throttle.key', + 'unknown' => 'Eroare neasteptata', ]; diff --git a/app/Language/ro/Reports.php b/app/Language/ro/Reports.php index 93316dfd2..15ff6bed9 100644 --- a/app/Language/ro/Reports.php +++ b/app/Language/ro/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/ru/Attributes.php b/app/Language/ru/Attributes.php index 08822fa57..f77199be3 100644 --- a/app/Language/ru/Attributes.php +++ b/app/Language/ru/Attributes.php @@ -1,34 +1,36 @@ "Значение атрибута не может содержать ':' или '|'", - "confirm_delete" => "Вы уверены, что хотите удалить выбранные атрибут(ы)?", - "confirm_restore" => "Вы уверены, что хотите восстановить выбранные атрибут(ы)?", - "definition_cannot_be_deleted" => "Не удалось удалить выбранные атрибут(ы)", - "definition_invalid_group" => "Выбранная группа не существует или недействительна.", - "definition_error_adding_updating" => "Атрибут {0} не может быть добавлен или обновлен. Пожалуйста, проверьте журнал ошибок.", - "definition_flags" => "Видимость атрибута", - "definition_group" => "Группа", - "definition_id" => "№", - "definition_name" => "Добавить атрибут", - "definition_name_required" => "Название атрибута - обязательное поле", - "definition_one_or_multiple" => "атрибут(ы)", - "definition_successful_adding" => "Вы успешно добавили товар", - "definition_successful_deleted" => "Успешно удалено", - "definition_successful_updating" => "Обновлено успешно", - "definition_type" => "Тип атрибута", - "definition_type_required" => "Название атрибута - обязательное поле", - "definition_unit" => "Единица измерения", - "definition_values" => "Значения атрибута", - "new" => "Новый атрибут", - "no_attributes_to_display" => "Нет товаров для отображения", - "receipt_visibility" => "Чек", - "show_in_items" => "Показать в товарах", - "show_in_items_visibility" => "Товары", - "show_in_receipt" => "Показать в квитанции", - "show_in_receivings" => "Показать в закупках", - "show_in_receivings_visibility" => "Закупки", - "show_in_sales" => "Показать в продажах", - "show_in_sales_visibility" => "Продажи", - "update" => "Обновить атрибут", + 'attribute_value_invalid_chars' => 'Значение атрибута не может содержать \':\' или \'|\'', + 'confirm_delete' => 'Вы уверены, что хотите удалить выбранные атрибут(ы)?', + 'confirm_restore' => 'Вы уверены, что хотите восстановить выбранные атрибут(ы)?', + 'definition_cannot_be_deleted' => 'Не удалось удалить выбранные атрибут(ы)', + 'definition_invalid_group' => 'Выбранная группа не существует или недействительна.', + 'definition_error_adding_updating' => 'Атрибут {0} не может быть добавлен или обновлен. Пожалуйста, проверьте журнал ошибок.', + 'definition_flags' => 'Видимость атрибута', + 'definition_group' => 'Группа', + 'definition_id' => '№', + 'definition_name' => 'Добавить атрибут', + 'definition_name_required' => 'Название атрибута - обязательное поле', + 'definition_one_or_multiple' => 'атрибут(ы)', + 'definition_successful_adding' => 'Вы успешно добавили товар', + 'definition_successful_deleted' => 'Успешно удалено', + 'definition_successful_updating' => 'Обновлено успешно', + 'definition_type' => 'Тип атрибута', + 'definition_type_required' => 'Название атрибута - обязательное поле', + 'definition_unit' => 'Единица измерения', + 'definition_values' => 'Значения атрибута', + 'new' => 'Новый атрибут', + 'no_attributes_to_display' => 'Нет товаров для отображения', + 'receipt_visibility' => 'Чек', + 'show_in_items' => 'Показать в товарах', + 'show_in_items_visibility' => 'Товары', + 'show_in_receipt' => 'Показать в квитанции', + 'show_in_receivings' => 'Показать в закупках', + 'show_in_receivings_visibility' => 'Закупки', + 'show_in_sales' => 'Показать в продажах', + 'show_in_sales_visibility' => 'Продажи', + 'show_in_search' => 'Показать в поиске', + 'show_in_search_visibility' => 'Поиск', + 'update' => 'Обновить атрибут', ]; diff --git a/app/Language/ru/Config.php b/app/Language/ru/Config.php index 79a24feb5..6a772bb41 100644 --- a/app/Language/ru/Config.php +++ b/app/Language/ru/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Сохраненные текстовые сообщения', 'msg_msg_placeholder' => 'Если вы хотите использовать шаблон SMS, сохраните свое сообщение здесь, в противном случае оставьте поле пустым.', 'msg_pwd' => 'Пароль SMS-API', - 'msg_pwd_required' => 'Пароль SMS-API - обязательное поле', 'msg_src' => 'ID отправителя SMS-API', 'msg_src_required' => 'ID отправителя SMS-API - обязательное поле', 'msg_uid' => 'Пользователь SMS-API', @@ -285,7 +284,7 @@ return [ 'right' => 'Справа', 'sales_invoice_format' => 'Формат накладной для продаж', 'sales_quote_format' => 'Формат предложений на продажу', - 'mailpath_invalid' => 'Неверный путь sendmail. Разрешены только буквы, цифры, дефисы, подчеркивания, слеши и точки.', + 'mailpath_invalid' => 'Неверный путь sendmail. Разрешены только буквы, цифры, дефисы, подчёркивания, слеши, обратные слеши, двоеточия, пробелы и точки.', 'saved_successfully' => 'Конфигурация успешно сохранена.', 'saved_unsuccessfully' => 'Произошла ошибка при сохранении конфигурации.', 'security_issue' => 'Предупреждение об уязвимости системы безопасности', diff --git a/app/Language/ru/Error.php b/app/Language/ru/Error.php index 1c097af52..39e767e00 100644 --- a/app/Language/ru/Error.php +++ b/app/Language/ru/Error.php @@ -1,6 +1,14 @@ "У вас нет разрешения на доступ к модулю", - "unknown" => "Неизвестная ошибка", + 'no_permission_module' => 'У вас нет разрешения на доступ к модулю', + 'unable_to_create_env_file' => 'Не удалось создать {filePath} для настройки throttle.key', + 'unable_to_lock_file' => 'Не удалось заблокировать {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Не удалось открыть {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Не удалось сохранить throttle.key в {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Не удалось прочитать {filePath} для настройки throttle.key', + 'unknown' => 'Неизвестная ошибка', ]; diff --git a/app/Language/sv/Attributes.php b/app/Language/sv/Attributes.php index 8efad3d00..3d9dac43c 100644 --- a/app/Language/sv/Attributes.php +++ b/app/Language/sv/Attributes.php @@ -1,34 +1,36 @@ "Attributvärdet får inte innehålla '_' eller '|'", - "confirm_delete" => "Är du säker på att du vill ta bort de valda attributen?", - "confirm_restore" => "Är du säker på att du vill återställa de valda attributen?", - "definition_cannot_be_deleted" => "Det gick inte att ta bort valda attribut", - "definition_invalid_group" => "Den valda gruppen finns inte eller är ogiltig.", - "definition_error_adding_updating" => "Attribut{0} kunde inte läggas till eller uppdateras. Kontrollera felloggen.", - "definition_flags" => "Attribut synlighet", - "definition_group" => "Grupp", - "definition_id" => "Id", - "definition_name" => "Lägg till attribut", - "definition_name_required" => "Attributnamn är ett obligatoriskt fält", - "definition_one_or_multiple" => "attribut", - "definition_successful_adding" => "Du har lagt till artikeln", - "definition_successful_deleted" => "Du har tagit bort", - "definition_successful_updating" => "Du har uppdaterat attributet", - "definition_type" => "Attributtyp", - "definition_type_required" => "Attributtyp är ett obligatoriskt fält", - "definition_unit" => "Måttenhet", - "definition_values" => "Attributvärden", - "new" => "Nytt attribut", - "no_attributes_to_display" => "Inga artiklar att visa", - "receipt_visibility" => "Kvitto", - "show_in_items" => "Visa i artiklar", - "show_in_items_visibility" => "Artiklar", - "show_in_receipt" => "Visa i kvitto", - "show_in_receivings" => "Visa i mottagande", - "show_in_receivings_visibility" => "Inleveranser", - "show_in_sales" => "Visa i försäljning", - "show_in_sales_visibility" => "Försäljning", - "update" => "Uppdatera attribut", + 'attribute_value_invalid_chars' => 'Attributvärdet får inte innehålla \'_\' eller \'|\'', + 'confirm_delete' => 'Är du säker på att du vill ta bort de valda attributen?', + 'confirm_restore' => 'Är du säker på att du vill återställa de valda attributen?', + 'definition_cannot_be_deleted' => 'Det gick inte att ta bort valda attribut', + 'definition_invalid_group' => 'Den valda gruppen finns inte eller är ogiltig.', + 'definition_error_adding_updating' => 'Attribut{0} kunde inte läggas till eller uppdateras. Kontrollera felloggen.', + 'definition_flags' => 'Attribut synlighet', + 'definition_group' => 'Grupp', + 'definition_id' => 'Id', + 'definition_name' => 'Lägg till attribut', + 'definition_name_required' => 'Attributnamn är ett obligatoriskt fält', + 'definition_one_or_multiple' => 'attribut', + 'definition_successful_adding' => 'Du har lagt till artikeln', + 'definition_successful_deleted' => 'Du har tagit bort', + 'definition_successful_updating' => 'Du har uppdaterat attributet', + 'definition_type' => 'Attributtyp', + 'definition_type_required' => 'Attributtyp är ett obligatoriskt fält', + 'definition_unit' => 'Måttenhet', + 'definition_values' => 'Attributvärden', + 'new' => 'Nytt attribut', + 'no_attributes_to_display' => 'Inga artiklar att visa', + 'receipt_visibility' => 'Kvitto', + 'show_in_items' => 'Visa i artiklar', + 'show_in_items_visibility' => 'Artiklar', + 'show_in_receipt' => 'Visa i kvitto', + 'show_in_receivings' => 'Visa i mottagande', + 'show_in_receivings_visibility' => 'Inleveranser', + 'show_in_sales' => 'Visa i försäljning', + 'show_in_sales_visibility' => 'Försäljning', + 'show_in_search' => 'Visa i sökning', + 'show_in_search_visibility' => 'Sökning', + 'update' => 'Uppdatera attribut', ]; diff --git a/app/Language/sv/Config.php b/app/Language/sv/Config.php index 0a817eb4c..af545ad2e 100644 --- a/app/Language/sv/Config.php +++ b/app/Language/sv/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Sparade SMS', 'msg_msg_placeholder' => 'Om du vill använda en SMS-mall, spara ditt meddelande här, annars lämna rutan tomt.', 'msg_pwd' => 'SMS-API-lösenord', - 'msg_pwd_required' => 'SMS-API lösenord är ett obligatoriskt fält', 'msg_src' => 'SMS-API Sender-ID', 'msg_src_required' => 'SMS-API Sender-ID är ett obligatoriskt fält', 'msg_uid' => 'SMS-API Användarnamn', @@ -285,7 +284,7 @@ return [ 'right' => 'Höger', 'sales_invoice_format' => 'Försäljningsfakturaformat', 'sales_quote_format' => 'Försäljningsquotaformat', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Ogiltig sendmail-sökväg. Endast bokstäver, siffror, bindestreck, understreck, snedstreck, omvända snedstreck, kolon, mellanslag och punkter tillåts.', 'saved_successfully' => 'Konfigurationen sparades.', 'saved_unsuccessfully' => 'Konfigurationsbesparingen misslyckades.', 'security_issue' => 'Varning för säkerhetsrisker', diff --git a/app/Language/sv/Error.php b/app/Language/sv/Error.php index 2a6a0caaa..ef99bea07 100644 --- a/app/Language/sv/Error.php +++ b/app/Language/sv/Error.php @@ -1,6 +1,14 @@ "Du har inte rättigheter till modulen", - "unknown" => "Oväntat fel", + 'no_permission_module' => 'Du har inte rättigheter till modulen', + 'unable_to_create_env_file' => 'Det gick inte att skapa {filePath} för att etablera throttle.key', + 'unable_to_lock_file' => 'Det gick inte att låsa {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Det gick inte att öppna {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Det gick inte att spara throttle.key i {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Det gick inte att läsa {filePath} för att etablera throttle.key', + 'unknown' => 'Oväntat fel', ]; diff --git a/app/Language/sv/Reports.php b/app/Language/sv/Reports.php index 68f79b1a3..232531637 100644 --- a/app/Language/sv/Reports.php +++ b/app/Language/sv/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Skattesats", "taxes" => "Skatter", "taxes_summary_report" => "Sammanfattningsrapport för skatter", + "toggle_cost_and_profit" => "", "total" => "Totalt", "total_inventory_value" => "Totalt lagervärde", "total_low_sell_quantity" => "Totalt lågt säljare antal", diff --git a/app/Language/sw-KE/Attributes.php b/app/Language/sw-KE/Attributes.php index 241521565..07927944d 100644 --- a/app/Language/sw-KE/Attributes.php +++ b/app/Language/sw-KE/Attributes.php @@ -1,34 +1,36 @@ "Thamani ya sifa haiwezi kuwa na '_' au '|'", - "confirm_delete" => "Una uhakika unataka kufuta sifa iliyochaguliwa/zilizochaguliwa?", - "confirm_restore" => "Una uhakika unataka kurejesha sifa iliyochaguliwa/zilizochaguliwa?", - "definition_cannot_be_deleted" => "Haiwezekani kufuta sifa iliyochaguliwa/zilizochaguliwa", - "definition_invalid_group" => "Kikundi ulichochagua hakipo au hakitoshi.", - "definition_error_adding_updating" => "Sifa {0} haiwezekani kuongezwa au kusasishwa. Tafadhali angalia logi ya makosa.", - "definition_flags" => "Uonekano wa Sifa", - "definition_group" => "Kundi", - "definition_id" => "Id", - "definition_name" => "Ongeza Sifa", - "definition_name_required" => "Jina la sifa ni kiashiria kinachohitajika", - "definition_one_or_multiple" => "Sifa", - "definition_successful_adding" => "Umefanikiwa kuongeza kipengee(Item)", - "definition_successful_deleted" => "Umefanikiwa kufuta", - "definition_successful_updating" => "Umefanikiwa kusasisha sifa", - "definition_type" => "Aina ya Sifa", - "definition_type_required" => "Aina ya sifa ni kiashiria kinachohitajika", - "definition_unit" => "Kipimo", - "definition_values" => "Thamani za Sifa", - "new" => "Sifa Mpya", - "no_attributes_to_display" => "Hakuna Sifa za kuonyesha", - "receipt_visibility" => "Risiti", - "show_in_items" => "Onyesha kwenye bidhaa", - "show_in_items_visibility" => "Bidhaa", - "show_in_receipt" => "Onyesha kwenye risiti", - "show_in_receivings" => "Onyesha kwenye Manunuzi", - "show_in_receivings_visibility" => "Manunuzi", - "show_in_sales" => "Onyesha kwenye Mauzo", - "show_in_sales_visibility" => "Mauzo", - "update" => "Sasisha Sifa", -]; \ No newline at end of file + 'attribute_value_invalid_chars' => 'Thamani ya sifa haiwezi kuwa na \'_\' au \'|\'', + 'confirm_delete' => 'Una uhakika unataka kufuta sifa iliyochaguliwa/zilizochaguliwa?', + 'confirm_restore' => 'Una uhakika unataka kurejesha sifa iliyochaguliwa/zilizochaguliwa?', + 'definition_cannot_be_deleted' => 'Haiwezekani kufuta sifa iliyochaguliwa/zilizochaguliwa', + 'definition_invalid_group' => 'Kikundi ulichochagua hakipo au hakitoshi.', + 'definition_error_adding_updating' => 'Sifa {0} haiwezekani kuongezwa au kusasishwa. Tafadhali angalia logi ya makosa.', + 'definition_flags' => 'Uonekano wa Sifa', + 'definition_group' => 'Kundi', + 'definition_id' => 'Id', + 'definition_name' => 'Ongeza Sifa', + 'definition_name_required' => 'Jina la sifa ni kiashiria kinachohitajika', + 'definition_one_or_multiple' => 'Sifa', + 'definition_successful_adding' => 'Umefanikiwa kuongeza kipengee(Item)', + 'definition_successful_deleted' => 'Umefanikiwa kufuta', + 'definition_successful_updating' => 'Umefanikiwa kusasisha sifa', + 'definition_type' => 'Aina ya Sifa', + 'definition_type_required' => 'Aina ya sifa ni kiashiria kinachohitajika', + 'definition_unit' => 'Kipimo', + 'definition_values' => 'Thamani za Sifa', + 'new' => 'Sifa Mpya', + 'no_attributes_to_display' => 'Hakuna Sifa za kuonyesha', + 'receipt_visibility' => 'Risiti', + 'show_in_items' => 'Onyesha kwenye bidhaa', + 'show_in_items_visibility' => 'Bidhaa', + 'show_in_receipt' => 'Onyesha kwenye risiti', + 'show_in_receivings' => 'Onyesha kwenye Manunuzi', + 'show_in_receivings_visibility' => 'Manunuzi', + 'show_in_sales' => 'Onyesha kwenye Mauzo', + 'show_in_sales_visibility' => 'Mauzo', + 'show_in_search' => 'Onyesha kwenye Utafutaji', + 'show_in_search_visibility' => 'Utafutaji', + 'update' => 'Sasisha Sifa', +]; diff --git a/app/Language/sw-KE/Config.php b/app/Language/sw-KE/Config.php index 8d5f46256..8ca0caf26 100644 --- a/app/Language/sw-KE/Config.php +++ b/app/Language/sw-KE/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Ujumbe wa SMS uliohifadhiwa', 'msg_msg_placeholder' => 'Ikiwa unataka kutumia kiolezo cha SMS hifadhi ujumbe wako hapa, vinginevyo acha kisanduku wazi.', 'msg_pwd' => 'Nenosiri la SMS-API', - 'msg_pwd_required' => 'Nenosiri la SMS-API ni kiashiria kinachohitajika', 'msg_src' => 'ID ya Mtumaji wa SMS-API', 'msg_src_required' => 'ID ya Mtumaji wa SMS-API ni kiashiria kinachohitajika', 'msg_uid' => 'Jina la Mtumiaji la SMS-API', @@ -285,7 +284,7 @@ return [ 'right' => 'Kulia', 'sales_invoice_format' => 'Muundo wa Ankara ya Mauzo', 'sales_quote_format' => 'Muundo wa Nukuu ya Mauzo', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Njia ya sendmail si sahihi. Herufi, nambari, mistari mifupi, mistari chini, mikwaju, mikwaju ya kinyume, koloni, nafasi na vitone pekee ndivyo vinaruhusiwa.', 'saved_successfully' => 'Mpangilio umehifadhiwa kwa mafanikio.', 'saved_unsuccessfully' => 'Mpangilio umeshindwa kuhifadhiwa.', 'security_issue' => 'Onyo la Udhaifu wa Usalama', diff --git a/app/Language/sw-KE/Error.php b/app/Language/sw-KE/Error.php index 46b3d5d83..40e039a9b 100644 --- a/app/Language/sw-KE/Error.php +++ b/app/Language/sw-KE/Error.php @@ -1,6 +1,14 @@ "Huna ruhusa ya kufikia moduli iliyoitwa", - "unknown" => "Hitilafu isiyotarajiwa", -]; \ No newline at end of file + 'no_permission_module' => 'Huna ruhusa ya kufikia moduli iliyoitwa', + 'unable_to_create_env_file' => 'Imeshindwa kuunda {filePath} kutayarisha throttle.key', + 'unable_to_lock_file' => 'Imeshindwa kufunga {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Imeshindwa kufungua {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Imeshindwa kuhifadhi throttle.key katika {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Imeshindwa kusoma {filePath} kutayarisha throttle.key', + 'unknown' => 'Hitilafu isiyotarajiwa', +]; diff --git a/app/Language/sw-TZ/Attributes.php b/app/Language/sw-TZ/Attributes.php index 241521565..07927944d 100644 --- a/app/Language/sw-TZ/Attributes.php +++ b/app/Language/sw-TZ/Attributes.php @@ -1,34 +1,36 @@ "Thamani ya sifa haiwezi kuwa na '_' au '|'", - "confirm_delete" => "Una uhakika unataka kufuta sifa iliyochaguliwa/zilizochaguliwa?", - "confirm_restore" => "Una uhakika unataka kurejesha sifa iliyochaguliwa/zilizochaguliwa?", - "definition_cannot_be_deleted" => "Haiwezekani kufuta sifa iliyochaguliwa/zilizochaguliwa", - "definition_invalid_group" => "Kikundi ulichochagua hakipo au hakitoshi.", - "definition_error_adding_updating" => "Sifa {0} haiwezekani kuongezwa au kusasishwa. Tafadhali angalia logi ya makosa.", - "definition_flags" => "Uonekano wa Sifa", - "definition_group" => "Kundi", - "definition_id" => "Id", - "definition_name" => "Ongeza Sifa", - "definition_name_required" => "Jina la sifa ni kiashiria kinachohitajika", - "definition_one_or_multiple" => "Sifa", - "definition_successful_adding" => "Umefanikiwa kuongeza kipengee(Item)", - "definition_successful_deleted" => "Umefanikiwa kufuta", - "definition_successful_updating" => "Umefanikiwa kusasisha sifa", - "definition_type" => "Aina ya Sifa", - "definition_type_required" => "Aina ya sifa ni kiashiria kinachohitajika", - "definition_unit" => "Kipimo", - "definition_values" => "Thamani za Sifa", - "new" => "Sifa Mpya", - "no_attributes_to_display" => "Hakuna Sifa za kuonyesha", - "receipt_visibility" => "Risiti", - "show_in_items" => "Onyesha kwenye bidhaa", - "show_in_items_visibility" => "Bidhaa", - "show_in_receipt" => "Onyesha kwenye risiti", - "show_in_receivings" => "Onyesha kwenye Manunuzi", - "show_in_receivings_visibility" => "Manunuzi", - "show_in_sales" => "Onyesha kwenye Mauzo", - "show_in_sales_visibility" => "Mauzo", - "update" => "Sasisha Sifa", -]; \ No newline at end of file + 'attribute_value_invalid_chars' => 'Thamani ya sifa haiwezi kuwa na \'_\' au \'|\'', + 'confirm_delete' => 'Una uhakika unataka kufuta sifa iliyochaguliwa/zilizochaguliwa?', + 'confirm_restore' => 'Una uhakika unataka kurejesha sifa iliyochaguliwa/zilizochaguliwa?', + 'definition_cannot_be_deleted' => 'Haiwezekani kufuta sifa iliyochaguliwa/zilizochaguliwa', + 'definition_invalid_group' => 'Kikundi ulichochagua hakipo au hakitoshi.', + 'definition_error_adding_updating' => 'Sifa {0} haiwezekani kuongezwa au kusasishwa. Tafadhali angalia logi ya makosa.', + 'definition_flags' => 'Uonekano wa Sifa', + 'definition_group' => 'Kundi', + 'definition_id' => 'Id', + 'definition_name' => 'Ongeza Sifa', + 'definition_name_required' => 'Jina la sifa ni kiashiria kinachohitajika', + 'definition_one_or_multiple' => 'Sifa', + 'definition_successful_adding' => 'Umefanikiwa kuongeza kipengee(Item)', + 'definition_successful_deleted' => 'Umefanikiwa kufuta', + 'definition_successful_updating' => 'Umefanikiwa kusasisha sifa', + 'definition_type' => 'Aina ya Sifa', + 'definition_type_required' => 'Aina ya sifa ni kiashiria kinachohitajika', + 'definition_unit' => 'Kipimo', + 'definition_values' => 'Thamani za Sifa', + 'new' => 'Sifa Mpya', + 'no_attributes_to_display' => 'Hakuna Sifa za kuonyesha', + 'receipt_visibility' => 'Risiti', + 'show_in_items' => 'Onyesha kwenye bidhaa', + 'show_in_items_visibility' => 'Bidhaa', + 'show_in_receipt' => 'Onyesha kwenye risiti', + 'show_in_receivings' => 'Onyesha kwenye Manunuzi', + 'show_in_receivings_visibility' => 'Manunuzi', + 'show_in_sales' => 'Onyesha kwenye Mauzo', + 'show_in_sales_visibility' => 'Mauzo', + 'show_in_search' => 'Onyesha kwenye Utafutaji', + 'show_in_search_visibility' => 'Utafutaji', + 'update' => 'Sasisha Sifa', +]; diff --git a/app/Language/sw-TZ/Config.php b/app/Language/sw-TZ/Config.php index 8d5f46256..5e2692474 100644 --- a/app/Language/sw-TZ/Config.php +++ b/app/Language/sw-TZ/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Ujumbe wa SMS uliohifadhiwa', 'msg_msg_placeholder' => 'Ikiwa unataka kutumia kiolezo cha SMS hifadhi ujumbe wako hapa, vinginevyo acha kisanduku wazi.', 'msg_pwd' => 'Nenosiri la SMS-API', - 'msg_pwd_required' => 'Nenosiri la SMS-API ni kiashiria kinachohitajika', 'msg_src' => 'ID ya Mtumaji wa SMS-API', 'msg_src_required' => 'ID ya Mtumaji wa SMS-API ni kiashiria kinachohitajika', 'msg_uid' => 'Jina la Mtumiaji la SMS-API', @@ -285,7 +284,7 @@ return [ 'right' => 'Kulia', 'sales_invoice_format' => 'Muundo wa Ankara ya Mauzo', 'sales_quote_format' => 'Muundo wa Nukuu ya Mauzo', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Njia ya sendmail si sahihi. Herufi, tarakimu, mistari mifupi, mistari ya chini, mikwaju, mikwaju ya kinyume, koloni, nafasi na vitone tu ndivyo vinaruhusiwa.', 'saved_successfully' => 'Mpangilio umehifadhiwa kwa mafanikio.', 'saved_unsuccessfully' => 'Mpangilio umeshindwa kuhifadhiwa.', 'security_issue' => 'Onyo la Udhaifu wa Usalama', diff --git a/app/Language/sw-TZ/Error.php b/app/Language/sw-TZ/Error.php index 46b3d5d83..40e039a9b 100644 --- a/app/Language/sw-TZ/Error.php +++ b/app/Language/sw-TZ/Error.php @@ -1,6 +1,14 @@ "Huna ruhusa ya kufikia moduli iliyoitwa", - "unknown" => "Hitilafu isiyotarajiwa", -]; \ No newline at end of file + 'no_permission_module' => 'Huna ruhusa ya kufikia moduli iliyoitwa', + 'unable_to_create_env_file' => 'Imeshindwa kuunda {filePath} kutayarisha throttle.key', + 'unable_to_lock_file' => 'Imeshindwa kufunga {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Imeshindwa kufungua {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Imeshindwa kuhifadhi throttle.key katika {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Imeshindwa kusoma {filePath} kutayarisha throttle.key', + 'unknown' => 'Hitilafu isiyotarajiwa', +]; diff --git a/app/Language/ta/Attributes.php b/app/Language/ta/Attributes.php index 9c278989d..c944cc3ea 100644 --- a/app/Language/ta/Attributes.php +++ b/app/Language/ta/Attributes.php @@ -1,34 +1,36 @@ "பண்புக்கூறு மதிப்பு '_' அல்லது '|' கொண்டிருக்கக்கூடாது", - "confirm_delete" => "தேர்ந்தெடுக்கப்பட்ட பண்புக்கூறு (களை) நீக்க விரும்புகிறீர்களா?", - "confirm_restore" => "தேர்ந்தெடுக்கப்பட்ட பண்புக்கூறுகளை (களை) மீட்டெடுக்க விரும்புகிறீர்களா?", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'பண்புக்கூறு மதிப்பு \'_\' அல்லது \'|\' கொண்டிருக்கக்கூடாது', + 'confirm_delete' => 'தேர்ந்தெடுக்கப்பட்ட பண்புக்கூறு (களை) நீக்க விரும்புகிறீர்களா?', + 'confirm_restore' => 'தேர்ந்தெடுக்கப்பட்ட பண்புக்கூறுகளை (களை) மீட்டெடுக்க விரும்புகிறீர்களா?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute name is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated attribute', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute type is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Items to display', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'தேடலில் காட்டு', + 'show_in_search_visibility' => 'தேடல்', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/ta/Config.php b/app/Language/ta/Config.php index 84c33fd33..c06a4d27e 100644 --- a/app/Language/ta/Config.php +++ b/app/Language/ta/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -237,8 +236,8 @@ return [ 'print_bottom_margin_number' => 'Margin Bottom must be a number.', 'print_bottom_margin_required' => 'Margin Bottom is a required field.', 'print_delay_autoreturn' => 'Autoreturn to Sale delay', - 'print_delay_autoreturn_number' => 'Autoreturn to Sale delay is a required field.', - 'print_delay_autoreturn_required' => 'Autoreturn to Sale delay must be a number.', + 'print_delay_autoreturn_number' => '', + 'print_delay_autoreturn_required' => '', 'print_footer' => 'Print Browser Footer', 'print_header' => 'Print Browser Header', 'print_left_margin' => 'Margin Left', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'தவறான sendmail பாதை. எழுத்துகள், எண்கள், கோடுகள், அடிக்கோடுகள், சாய்வுக்கோடுகள், பின்சாய்வுக்கோடுகள், முக்காற்புள்ளிகள், இடைவெளிகள் மற்றும் புள்ளிகள் மட்டுமே அனுமதிக்கப்படும்.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/ta/Error.php b/app/Language/ta/Error.php index 7e7b2189c..41838b567 100644 --- a/app/Language/ta/Error.php +++ b/app/Language/ta/Error.php @@ -1,6 +1,14 @@ "பெயரிடப்பட்ட தொகுதியை பயன்படுத்த உங்களுக்கு அனுமதி இல்லை", - "unknown" => "எதிர்பாராத பிழை", + 'no_permission_module' => 'பெயரிடப்பட்ட தொகுதியை பயன்படுத்த உங்களுக்கு அனுமதி இல்லை', + 'unable_to_create_env_file' => 'throttle.key ஐ ஏற்பாடு செய்ய {filePath} ஐ உருவாக்க முடியவில்லை', + 'unable_to_lock_file' => '{filePath} ஐ பூட்ட முடியவில்லை: {reason}', + 'unable_to_open_lock_file' => '{filePath} ஐ திறக்க முடியவில்லை: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key ஐ {filePath} இல் நிலைநிறுத்த முடியவில்லை', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'throttle.key ஐ ஏற்பாடு செய்ய {filePath} ஐ படிக்க முடியவில்லை', + 'unknown' => 'எதிர்பாராத பிழை', ]; diff --git a/app/Language/ta/Reports.php b/app/Language/ta/Reports.php index 1a4614bf2..826ffc417 100644 --- a/app/Language/ta/Reports.php +++ b/app/Language/ta/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Tax Rate", "taxes" => "Taxes", "taxes_summary_report" => "Taxes Summary Report", + "toggle_cost_and_profit" => "", "total" => "Total", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "Total Low Sell Quantity", diff --git a/app/Language/th/Attributes.php b/app/Language/th/Attributes.php index 4c1e161b3..3d10767f0 100644 --- a/app/Language/th/Attributes.php +++ b/app/Language/th/Attributes.php @@ -1,34 +1,36 @@ "คุณลักษณะไม่สามารถมีเครื่องหมาย ':' หรือเครื่องหมาย '|' ได้", - "confirm_delete" => "ต้องการลบคุณลักษณะที่เลือกหรือไม่ ?", - "confirm_restore" => "ต้องการคืนค่าคุณลักษณะที่เลือกหรือไม่ ?", - "definition_cannot_be_deleted" => "ไม่สามารถลบคุณลักษณะที่เลือก", - "definition_invalid_group" => "กลุ่มที่เลือกไม่มีอยู่หรือไม่ถูกต้อง", - "definition_error_adding_updating" => "ไม่สามารถเพิ่มหรือแก้ไขคุณลักษณะ {0}, โปรดตรวจสอบความผิดพลาดในบันทึก", - "definition_flags" => "การมองเห็นคุณลักษณะ", - "definition_group" => "กลุ่ม", - "definition_id" => "ID", - "definition_name" => "เพิ่มแอตทริบิวต์", - "definition_name_required" => "ชื่อแอตทริบิวต์ จำเป็นต้องป้อน", - "definition_one_or_multiple" => "แอตทริบิวต์", - "definition_successful_adding" => "เพิ่มรายการสำเร็จแล้ว", - "definition_successful_deleted" => "ลบรายการสำเร็จแล้ว", - "definition_successful_updating" => "อัปเดตแอตทริบิวต์สำเร็จแล้ว", - "definition_type" => "ประเภทแอตทริบิวต์", - "definition_type_required" => "ประเภทแอตทริบิวต์ จำเป็นต้องป้อน", - "definition_unit" => "หน่วยวัด", - "definition_values" => "ค่าแอตทริบิวต์", - "new" => "สร้าง แอตทริบิวต์", - "no_attributes_to_display" => "ไม่มีรายการที่จะแสดง", - "receipt_visibility" => "ใบเสร็จ", - "show_in_items" => "แสดงใน รายการสินค้า", - "show_in_items_visibility" => "รายการสินค้า", - "show_in_receipt" => "แสดงใน ใบเสร็จ", - "show_in_receivings" => "แสดงใน สินค้าขาเข้า", - "show_in_receivings_visibility" => "สินค้าขาเข้า", - "show_in_sales" => "แสดงใน การขาย", - "show_in_sales_visibility" => "การขาย", - "update" => "ปรับปรุงแอตทริบิวต์", + 'attribute_value_invalid_chars' => 'คุณลักษณะไม่สามารถมีเครื่องหมาย \':\' หรือเครื่องหมาย \'|\' ได้', + 'confirm_delete' => 'ต้องการลบคุณลักษณะที่เลือกหรือไม่ ?', + 'confirm_restore' => 'ต้องการคืนค่าคุณลักษณะที่เลือกหรือไม่ ?', + 'definition_cannot_be_deleted' => 'ไม่สามารถลบคุณลักษณะที่เลือก', + 'definition_invalid_group' => 'กลุ่มที่เลือกไม่มีอยู่หรือไม่ถูกต้อง', + 'definition_error_adding_updating' => 'ไม่สามารถเพิ่มหรือแก้ไขคุณลักษณะ {0}, โปรดตรวจสอบความผิดพลาดในบันทึก', + 'definition_flags' => 'การมองเห็นคุณลักษณะ', + 'definition_group' => 'กลุ่ม', + 'definition_id' => 'ID', + 'definition_name' => 'เพิ่มแอตทริบิวต์', + 'definition_name_required' => 'ชื่อแอตทริบิวต์ จำเป็นต้องป้อน', + 'definition_one_or_multiple' => 'แอตทริบิวต์', + 'definition_successful_adding' => 'เพิ่มรายการสำเร็จแล้ว', + 'definition_successful_deleted' => 'ลบรายการสำเร็จแล้ว', + 'definition_successful_updating' => 'อัปเดตแอตทริบิวต์สำเร็จแล้ว', + 'definition_type' => 'ประเภทแอตทริบิวต์', + 'definition_type_required' => 'ประเภทแอตทริบิวต์ จำเป็นต้องป้อน', + 'definition_unit' => 'หน่วยวัด', + 'definition_values' => 'ค่าแอตทริบิวต์', + 'new' => 'สร้าง แอตทริบิวต์', + 'no_attributes_to_display' => 'ไม่มีรายการที่จะแสดง', + 'receipt_visibility' => 'ใบเสร็จ', + 'show_in_items' => 'แสดงใน รายการสินค้า', + 'show_in_items_visibility' => 'รายการสินค้า', + 'show_in_receipt' => 'แสดงใน ใบเสร็จ', + 'show_in_receivings' => 'แสดงใน สินค้าขาเข้า', + 'show_in_receivings_visibility' => 'สินค้าขาเข้า', + 'show_in_sales' => 'แสดงใน การขาย', + 'show_in_sales_visibility' => 'การขาย', + 'show_in_search' => 'แสดงใน การค้นหา', + 'show_in_search_visibility' => 'การค้นหา', + 'update' => 'ปรับปรุงแอตทริบิวต์', ]; diff --git a/app/Language/th/Config.php b/app/Language/th/Config.php index 23144e024..7cc25761e 100644 --- a/app/Language/th/Config.php +++ b/app/Language/th/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'ข้อความที่ถูกบักทึกไว้', 'msg_msg_placeholder' => 'สร้างข้อความ SMS template ที่นี่', 'msg_pwd' => 'รหัสผ่านของ SMS-API', - 'msg_pwd_required' => 'จำเป็นต้องป้อน รหัสผ่านของ SMS-API', 'msg_src' => 'ID ผู้ส่ง (SMS-API)', 'msg_src_required' => 'จำเป็นต้องป้อน ID ผู้ส่ง', 'msg_uid' => 'ชื่อผู้ใช้งานระบบ SMS-API', @@ -285,7 +284,7 @@ return [ 'right' => 'ถูกต้อง', 'sales_invoice_format' => 'รหัสใบเสร็จ', 'sales_quote_format' => 'รูปแบบใบเสนอราคาขาย', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'เส้นทาง sendmail ไม่ถูกต้อง อนุญาตเฉพาะตัวอักษร ตัวเลข ขีดกลาง ขีดล่าง เครื่องหมายทับ เครื่องหมายทับกลับ โคลอน ช่องว่าง และจุดเท่านั้น', 'saved_successfully' => 'บันทึกข้อมูลร้านค้าเรียบร้อยแล้ว', 'saved_unsuccessfully' => 'บันทึกข้อมูลร้านค้าไม่สำเร็จ', 'security_issue' => 'คำเตือนช่องโหว่ด้านความปลอดภัย', diff --git a/app/Language/th/Error.php b/app/Language/th/Error.php index 70a2c033b..0944e4427 100644 --- a/app/Language/th/Error.php +++ b/app/Language/th/Error.php @@ -1,6 +1,14 @@ "คุณไม่ได้รับสิทธิ์การเข้าถึงข้อมูลในส่วนนี้", - "unknown" => "ไม่ทราบ", + 'no_permission_module' => 'คุณไม่ได้รับสิทธิ์การเข้าถึงข้อมูลในส่วนนี้', + 'unable_to_create_env_file' => 'ไม่สามารถสร้าง {filePath} เพื่อกำหนดค่า throttle.key ได้', + 'unable_to_lock_file' => 'ไม่สามารถล็อกไฟล์ {filePath} ได้: {reason}', + 'unable_to_open_lock_file' => 'ไม่สามารถเปิดไฟล์ {filePath} ได้: {reason}', + 'unable_to_persist_throttle_key' => 'ไม่สามารถบันทึก throttle.key ลงใน {filePath} ได้', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'ไม่สามารถอ่าน {filePath} เพื่อกำหนดค่า throttle.key ได้', + 'unknown' => 'ไม่ทราบ', ]; diff --git a/app/Language/tl/Attributes.php b/app/Language/tl/Attributes.php index e163b8835..cfa63c1d0 100644 --- a/app/Language/tl/Attributes.php +++ b/app/Language/tl/Attributes.php @@ -1,34 +1,36 @@ "Attribute value cannot contain ':' or '|'", - "confirm_delete" => "Are you sure you want to restore the selected attribute(s)?", - "confirm_restore" => "Are you sure you want to delete the selected attribute(s)?", - "definition_cannot_be_deleted" => "Could not delete selected attribute(s)", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute type is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated Item Kit", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute name is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display.", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'Attribute value cannot contain \':\' or \'|\'', + 'confirm_delete' => 'Are you sure you want to restore the selected attribute(s)?', + 'confirm_restore' => 'Are you sure you want to delete the selected attribute(s)?', + 'definition_cannot_be_deleted' => 'Could not delete selected attribute(s)', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute type is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated Item Kit', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute name is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Items to display.', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'Ipakita sa paghahanap', + 'show_in_search_visibility' => 'Paghahanap', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/tl/Config.php b/app/Language/tl/Config.php index 2e001dc93..fd5d75f7f 100644 --- a/app/Language/tl/Config.php +++ b/app/Language/tl/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here, otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -237,8 +236,8 @@ return [ 'print_bottom_margin_number' => 'Margin Left must be a number.', 'print_bottom_margin_required' => 'Margin Left is a required field.', 'print_delay_autoreturn' => 'Autoreturn to Sale delay', - 'print_delay_autoreturn_number' => 'Autoreturn to Sale delay is a required field.', - 'print_delay_autoreturn_required' => 'Autoreturn to Sale delay must be a number.', + 'print_delay_autoreturn_number' => '', + 'print_delay_autoreturn_required' => '', 'print_footer' => 'Print Browser Header', 'print_header' => 'Print Browser Footer', 'print_left_margin' => 'Margin Left', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => 'Sales Quote Format', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Hindi wastong landas ng sendmail. Mga titik, numero, gitling, underscore, slash, backslash, kolon, espasyo, at tuldok lamang ang pinapayagan.', 'saved_successfully' => 'Configuration save successful.', 'saved_unsuccessfully' => 'Configuration save failed.', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/tl/Error.php b/app/Language/tl/Error.php index ace67fbb1..b594b05db 100644 --- a/app/Language/tl/Error.php +++ b/app/Language/tl/Error.php @@ -1,6 +1,14 @@ "You do not have permission to access the module named", - "unknown" => "Unexpected error", + 'no_permission_module' => 'Wala kang pahintulot na gamitin ang module na pinangalanang', + 'unable_to_create_env_file' => 'Hindi magawa ang {filePath} para itakda ang throttle.key', + 'unable_to_lock_file' => 'Hindi ma-lock ang {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Hindi mabuksan ang {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Hindi ma-save ang throttle.key sa {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Hindi mabasa ang {filePath} para itakda ang throttle.key', + 'unknown' => 'Hindi inaasahang error', ]; diff --git a/app/Language/tl/Reports.php b/app/Language/tl/Reports.php index b62356a06..06cea79e7 100644 --- a/app/Language/tl/Reports.php +++ b/app/Language/tl/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Tax Rate", "taxes" => "Taxes", "taxes_summary_report" => "Taxes Summary Report", + "toggle_cost_and_profit" => "", "total" => "Total", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "Total Low Sell Quantity", diff --git a/app/Language/tr/Attributes.php b/app/Language/tr/Attributes.php index 877496fc7..570426e32 100644 --- a/app/Language/tr/Attributes.php +++ b/app/Language/tr/Attributes.php @@ -1,34 +1,36 @@ "Nitelik değeri ':' ve ya '|' karakterlerini bulunduramaz", - "confirm_delete" => "Seçili niteliği ya da nitelikleri silmek istediğinize emin misiniz?", - "confirm_restore" => "Seçili nitelik ya da nitelikleri kurtarmak istediğinize emin misiniz?", - "definition_cannot_be_deleted" => "Seçili nitelik ya da nitelikler silinemedi", - "definition_invalid_group" => "Seçilen grup mevcut değil veya geçersiz.", - "definition_error_adding_updating" => "Nitelik {0} eklenemedi ya da güncellenemedi. Lütfen hata kaydını gözden geçirin.", - "definition_flags" => "Nitelik Görünebilirliği", - "definition_group" => "Küme", - "definition_id" => "Kimlik", - "definition_name" => "Nitelik Ekle", - "definition_name_required" => "Nitelik adı girilmesi gerekli alandır", - "definition_one_or_multiple" => "Nitelik/Nitelikler", - "definition_successful_adding" => "Başarıyla öge eklediniz", - "definition_successful_deleted" => "Başarıyla sildiniz", - "definition_successful_updating" => "Başarılı biçimde niteliği güncellediniz", - "definition_type" => "Nitelik Türü", - "definition_type_required" => "Nitelik türü alanı gereklidir", - "definition_unit" => "Ölçü Birimi", - "definition_values" => "Nitelik Değerleri", - "new" => "Yeni Nitelik", - "no_attributes_to_display" => "Gösterecek ürün yok", - "receipt_visibility" => "Fiş", - "show_in_items" => "Ürünlerde göster", - "show_in_items_visibility" => "Ürünler", - "show_in_receipt" => "Fişlerde göster", - "show_in_receivings" => "Alacaklarda göster", - "show_in_receivings_visibility" => "Alacaklar", - "show_in_sales" => "Satışlarda göster", - "show_in_sales_visibility" => "Satışlar", - "update" => "Nitelik Güncelle", + 'attribute_value_invalid_chars' => 'Nitelik değeri \':\' ve ya \'|\' karakterlerini bulunduramaz', + 'confirm_delete' => 'Seçili niteliği ya da nitelikleri silmek istediğinize emin misiniz?', + 'confirm_restore' => 'Seçili nitelik ya da nitelikleri kurtarmak istediğinize emin misiniz?', + 'definition_cannot_be_deleted' => 'Seçili nitelik ya da nitelikler silinemedi', + 'definition_invalid_group' => 'Seçilen grup mevcut değil veya geçersiz.', + 'definition_error_adding_updating' => 'Nitelik {0} eklenemedi ya da güncellenemedi. Lütfen hata kaydını gözden geçirin.', + 'definition_flags' => 'Nitelik Görünebilirliği', + 'definition_group' => 'Küme', + 'definition_id' => 'Kimlik', + 'definition_name' => 'Nitelik Ekle', + 'definition_name_required' => 'Nitelik adı girilmesi gerekli alandır', + 'definition_one_or_multiple' => 'Nitelik/Nitelikler', + 'definition_successful_adding' => 'Başarıyla öge eklediniz', + 'definition_successful_deleted' => 'Başarıyla sildiniz', + 'definition_successful_updating' => 'Başarılı biçimde niteliği güncellediniz', + 'definition_type' => 'Nitelik Türü', + 'definition_type_required' => 'Nitelik türü alanı gereklidir', + 'definition_unit' => 'Ölçü Birimi', + 'definition_values' => 'Nitelik Değerleri', + 'new' => 'Yeni Nitelik', + 'no_attributes_to_display' => 'Gösterecek ürün yok', + 'receipt_visibility' => 'Fiş', + 'show_in_items' => 'Ürünlerde göster', + 'show_in_items_visibility' => 'Ürünler', + 'show_in_receipt' => 'Fişlerde göster', + 'show_in_receivings' => 'Alacaklarda göster', + 'show_in_receivings_visibility' => 'Alacaklar', + 'show_in_sales' => 'Satışlarda göster', + 'show_in_sales_visibility' => 'Satışlar', + 'show_in_search' => 'Aramada göster', + 'show_in_search_visibility' => 'Arama', + 'update' => 'Nitelik Güncelle', ]; diff --git a/app/Language/tr/Config.php b/app/Language/tr/Config.php index 851408f26..7619448c2 100644 --- a/app/Language/tr/Config.php +++ b/app/Language/tr/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Kaydedilen Metin İletisi', 'msg_msg_placeholder' => 'Eğer bir SMS şablonu kullanmak isterseniz iletinizi buraya kaydediniz. Ya da kutuyu boş bırakınız.', 'msg_pwd' => 'SMS-API Parolası', - 'msg_pwd_required' => 'SMS-API Parola zorunlu bir alandır', 'msg_src' => 'SMS-API Gönderici Kimliği', 'msg_src_required' => 'SMS-API Gönderici Kimliği zorunlu bir alandır', 'msg_uid' => 'SMS-API Kullanıcı Adı', @@ -285,7 +284,7 @@ return [ 'right' => 'Sağ', 'sales_invoice_format' => 'Satış Fatura Biçimi', 'sales_quote_format' => 'Satış Teklif Biçimi', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Geçersiz sendmail yolu. Yalnızca harflere, rakamlara, tirelere, alt çizgilere, eğik çizgilere, ters eğik çizgilere, iki noktalara, boşluklara ve noktalara izin verilir.', 'saved_successfully' => 'Yapılandırma kaydedildi.', 'saved_unsuccessfully' => 'Yapılandırma kaydedilemedi.', 'security_issue' => 'Güvenlik Arıklığı Uyarısı', diff --git a/app/Language/tr/Error.php b/app/Language/tr/Error.php index 28d640714..a4b7ba20b 100644 --- a/app/Language/tr/Error.php +++ b/app/Language/tr/Error.php @@ -1,6 +1,14 @@ "Bu modüle erişim yetkiniz yok", - "unknown" => "bilinmeyen", + 'no_permission_module' => 'Bu modüle erişim yetkiniz yok', + 'unable_to_create_env_file' => 'throttle.key değerini ayarlamak için {filePath} oluşturulamadı', + 'unable_to_lock_file' => '{filePath} kilitlenemedi: {reason}', + 'unable_to_open_lock_file' => '{filePath} açılamadı: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key, {filePath} içine kaydedilemedi', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'throttle.key değerini ayarlamak için {filePath} okunamadı', + 'unknown' => 'bilinmeyen', ]; diff --git a/app/Language/tr/Reports.php b/app/Language/tr/Reports.php index 9af63204b..71b4d73e8 100644 --- a/app/Language/tr/Reports.php +++ b/app/Language/tr/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Vergi oranı", "taxes" => "Vergiler", "taxes_summary_report" => "Vergi Özet Raporu", + "toggle_cost_and_profit" => "", "total" => "Toplam", "total_inventory_value" => "Toplam Stok Değeri", "total_low_sell_quantity" => "Toplam Düşük Satış Miktarı", diff --git a/app/Language/uk/Attributes.php b/app/Language/uk/Attributes.php index f9302f861..d0fa55e8a 100644 --- a/app/Language/uk/Attributes.php +++ b/app/Language/uk/Attributes.php @@ -1,34 +1,36 @@ "Значення атрибуту не може містити ':' або'|'", - "confirm_delete" => "Ви впевнені, що хочете видалити вибрані атрибут(и)?", - "confirm_restore" => "Ви впевнені, що хочете відновити вибрані атрибут(и)?", - "definition_cannot_be_deleted" => "Не вдалося видалити вибрані атрибут(и)", - "definition_invalid_group" => "Вибрана група не існує або недійсна.", - "definition_error_adding_updating" => "Атрибут {0} не може бути доданий або оновлений. Будь ласка, перевірте журнал помилок.", - "definition_flags" => "Видимість атрибуту", - "definition_group" => "Група", - "definition_id" => "№", - "definition_name" => "Додати атрибут", - "definition_name_required" => "Назва атрибуту - обов'язкове поле", - "definition_one_or_multiple" => "Атрибут(и)", - "definition_successful_adding" => "Ви успішно додали товар", - "definition_successful_deleted" => "Успішно видалено", - "definition_successful_updating" => "Оновлено успішно", - "definition_type" => "Тип атрибуту", - "definition_type_required" => "Назва атрибуту - обов'язкове поле", - "definition_unit" => "Одиниця виміру", - "definition_values" => "Значення атрибуту", - "new" => "Новий атрибут", - "no_attributes_to_display" => "Немає об'єктів для відображення", - "receipt_visibility" => "Чек", - "show_in_items" => "Показати в товарах", - "show_in_items_visibility" => "Товари", - "show_in_receipt" => "Показати в квитанції", - "show_in_receivings" => "Показати в надходженнях", - "show_in_receivings_visibility" => "Надходження", - "show_in_sales" => "Показати в продажах", - "show_in_sales_visibility" => "Продажі", - "update" => "Оновити атрибут", + 'attribute_value_invalid_chars' => 'Значення атрибуту не може містити \':\' або\'|\'', + 'confirm_delete' => 'Ви впевнені, що хочете видалити вибрані атрибут(и)?', + 'confirm_restore' => 'Ви впевнені, що хочете відновити вибрані атрибут(и)?', + 'definition_cannot_be_deleted' => 'Не вдалося видалити вибрані атрибут(и)', + 'definition_invalid_group' => 'Вибрана група не існує або недійсна.', + 'definition_error_adding_updating' => 'Атрибут {0} не може бути доданий або оновлений. Будь ласка, перевірте журнал помилок.', + 'definition_flags' => 'Видимість атрибуту', + 'definition_group' => 'Група', + 'definition_id' => '№', + 'definition_name' => 'Додати атрибут', + 'definition_name_required' => 'Назва атрибуту - обов\'язкове поле', + 'definition_one_or_multiple' => 'Атрибут(и)', + 'definition_successful_adding' => 'Ви успішно додали товар', + 'definition_successful_deleted' => 'Успішно видалено', + 'definition_successful_updating' => 'Оновлено успішно', + 'definition_type' => 'Тип атрибуту', + 'definition_type_required' => 'Назва атрибуту - обов\'язкове поле', + 'definition_unit' => 'Одиниця виміру', + 'definition_values' => 'Значення атрибуту', + 'new' => 'Новий атрибут', + 'no_attributes_to_display' => 'Немає об\'єктів для відображення', + 'receipt_visibility' => 'Чек', + 'show_in_items' => 'Показати в товарах', + 'show_in_items_visibility' => 'Товари', + 'show_in_receipt' => 'Показати в квитанції', + 'show_in_receivings' => 'Показати в надходженнях', + 'show_in_receivings_visibility' => 'Надходження', + 'show_in_sales' => 'Показати в продажах', + 'show_in_sales_visibility' => 'Продажі', + 'show_in_search' => 'Показати в пошуку', + 'show_in_search_visibility' => 'Пошук', + 'update' => 'Оновити атрибут', ]; diff --git a/app/Language/uk/Config.php b/app/Language/uk/Config.php index 2d29fb4db..6a46a8bd0 100644 --- a/app/Language/uk/Config.php +++ b/app/Language/uk/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Збережене текстове повідомлення', 'msg_msg_placeholder' => 'Якщо ви хочете використовувати шаблон SMS, збережіть своє повідомлення тут або залиште поле порожнім', 'msg_pwd' => 'Пароль SMS-API', - 'msg_pwd_required' => "Пароль SMS-API - обов'язкове поле", 'msg_src' => 'Ідентифікатор відправника SMS-API', 'msg_src_required' => "Ідентифікатор відправника SMS-API - обов'язкове поле", 'msg_uid' => "Ім'я користувача SMS-API", @@ -285,7 +284,7 @@ return [ 'right' => 'Право', 'sales_invoice_format' => 'Формат рахунків-фактур продажів', 'sales_quote_format' => 'Формат котирування продажів', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Невірний шлях sendmail. Дозволені лише літери, цифри, дефіси, підкреслення, коси риски, зворотні коси риски, двокрапки, пробіли та крапки.', 'saved_successfully' => 'Конфігурація успішно збережена', 'saved_unsuccessfully' => 'Помилка збереження конфігурації', 'security_issue' => 'Попередження про вразливість системи безпеки', diff --git a/app/Language/uk/Error.php b/app/Language/uk/Error.php index 0729636e8..618494c07 100644 --- a/app/Language/uk/Error.php +++ b/app/Language/uk/Error.php @@ -1,6 +1,14 @@ "У вас немає дозволу на доступ до модуля", - "unknown" => "Невідомий", + 'no_permission_module' => 'У вас немає дозволу на доступ до модуля', + 'unable_to_create_env_file' => 'Не вдалося створити {filePath} для налаштування throttle.key', + 'unable_to_lock_file' => 'Не вдалося заблокувати {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Не вдалося відкрити {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Не вдалося зберегти throttle.key у {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Не вдалося прочитати {filePath} для налаштування throttle.key', + 'unknown' => 'Невідомий', ]; diff --git a/app/Language/uk/Reports.php b/app/Language/uk/Reports.php index 88e2fb2f8..0d52ea2cb 100644 --- a/app/Language/uk/Reports.php +++ b/app/Language/uk/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Ставка податку", "taxes" => "Податки", "taxes_summary_report" => "Підсумковий звіт про податки", + "toggle_cost_and_profit" => "", "total" => "Сума", "total_inventory_value" => "Загальна вартість товарів", "total_low_sell_quantity" => "Загальна кількість низького рівня продажу", diff --git a/app/Language/ur/Attributes.php b/app/Language/ur/Attributes.php index ce02d9f2c..8b46fb4f6 100644 --- a/app/Language/ur/Attributes.php +++ b/app/Language/ur/Attributes.php @@ -1,34 +1,36 @@ "وصف کے اندراج میں ':' یا '|' ممنوع ہیں", - "confirm_delete" => "کیا آپ منتخب شدہ کو حذف کرنا چاہتے ہیں ؟", - "confirm_restore" => "کیا آپ منتخب شدہ کو بحال کرنا چاہتے ہیں ؟", - "definition_cannot_be_deleted" => "منتخب شدہ کو حذف نہیں کیا جا سکتا", - "definition_invalid_group" => "", - "definition_error_adding_updating" => "Attribute {0} could not be added or updated. Please check the error log.", - "definition_flags" => "Attribute Visibility", - "definition_group" => "Group", - "definition_id" => "Id", - "definition_name" => "Add Attribute", - "definition_name_required" => "Attribute name is a required field", - "definition_one_or_multiple" => "attribute(s)", - "definition_successful_adding" => "You have successfully added item", - "definition_successful_deleted" => "You have successfully deleted", - "definition_successful_updating" => "You have successfully updated attribute", - "definition_type" => "Attribute Type", - "definition_type_required" => "Attribute type is a required field", - "definition_unit" => "Measurement Unit", - "definition_values" => "Attribute Values", - "new" => "New Attribute", - "no_attributes_to_display" => "No Items to display", - "receipt_visibility" => "Receipt", - "show_in_items" => "Show in items", - "show_in_items_visibility" => "Items", - "show_in_receipt" => "Show in receipt", - "show_in_receivings" => "Show in receivings", - "show_in_receivings_visibility" => "Receivings", - "show_in_sales" => "Show in sales", - "show_in_sales_visibility" => "Sales", - "update" => "Update Attribute", + 'attribute_value_invalid_chars' => 'وصف کے اندراج میں \':\' یا \'|\' ممنوع ہیں', + 'confirm_delete' => 'کیا آپ منتخب شدہ کو حذف کرنا چاہتے ہیں ؟', + 'confirm_restore' => 'کیا آپ منتخب شدہ کو بحال کرنا چاہتے ہیں ؟', + 'definition_cannot_be_deleted' => 'منتخب شدہ کو حذف نہیں کیا جا سکتا', + 'definition_invalid_group' => '', + 'definition_error_adding_updating' => 'Attribute {0} could not be added or updated. Please check the error log.', + 'definition_flags' => 'Attribute Visibility', + 'definition_group' => 'Group', + 'definition_id' => 'Id', + 'definition_name' => 'Add Attribute', + 'definition_name_required' => 'Attribute name is a required field', + 'definition_one_or_multiple' => 'attribute(s)', + 'definition_successful_adding' => 'You have successfully added item', + 'definition_successful_deleted' => 'You have successfully deleted', + 'definition_successful_updating' => 'You have successfully updated attribute', + 'definition_type' => 'Attribute Type', + 'definition_type_required' => 'Attribute type is a required field', + 'definition_unit' => 'Measurement Unit', + 'definition_values' => 'Attribute Values', + 'new' => 'New Attribute', + 'no_attributes_to_display' => 'No Items to display', + 'receipt_visibility' => 'Receipt', + 'show_in_items' => 'Show in items', + 'show_in_items_visibility' => 'Items', + 'show_in_receipt' => 'Show in receipt', + 'show_in_receivings' => 'Show in receivings', + 'show_in_receivings_visibility' => 'Receivings', + 'show_in_sales' => 'Show in sales', + 'show_in_sales_visibility' => 'Sales', + 'show_in_search' => 'تلاش میں دکھائیں', + 'show_in_search_visibility' => 'تلاش', + 'update' => 'Update Attribute', ]; diff --git a/app/Language/ur/Config.php b/app/Language/ur/Config.php index cfcf8c0ff..3bab550bc 100644 --- a/app/Language/ur/Config.php +++ b/app/Language/ur/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '', 'msg_msg_placeholder' => '', 'msg_pwd' => '', - 'msg_pwd_required' => '', 'msg_src' => '', 'msg_src_required' => '', 'msg_uid' => '', @@ -285,7 +284,7 @@ return [ 'right' => '', 'sales_invoice_format' => '', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'sendmail کا راستہ غلط ہے۔ صرف حروف، ہندسے، ڈیش، انڈر اسکور، سلیش، بیک سلیش، کالن، اسپیس اور نقطے کی اجازت ہے۔', 'saved_successfully' => '', 'saved_unsuccessfully' => '', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/ur/Error.php b/app/Language/ur/Error.php index 54b55d655..98645da40 100644 --- a/app/Language/ur/Error.php +++ b/app/Language/ur/Error.php @@ -1,6 +1,14 @@ "آپ کو اس ماڈیول تک رسائی کی اجازت نہیں ہے", - "unknown" => "غیر متوقع رکاوٹ", + 'no_permission_module' => 'آپ کو اس ماڈیول تک رسائی کی اجازت نہیں ہے', + 'unable_to_create_env_file' => 'throttle.key ترتیب دینے کے لیے {filePath} نہیں بنائی جا سکی', + 'unable_to_lock_file' => '{filePath} کو لاک نہیں کیا جا سکا: {reason}', + 'unable_to_open_lock_file' => '{filePath} کو کھولا نہیں جا سکا: {reason}', + 'unable_to_persist_throttle_key' => 'throttle.key کو {filePath} میں محفوظ نہیں کیا جا سکا', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'throttle.key ترتیب دینے کے لیے {filePath} پڑھی نہیں جا سکی', + 'unknown' => 'غیر متوقع رکاوٹ', ]; diff --git a/app/Language/ur/Reports.php b/app/Language/ur/Reports.php index 93316dfd2..15ff6bed9 100644 --- a/app/Language/ur/Reports.php +++ b/app/Language/ur/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "", "taxes_summary_report" => "", + "toggle_cost_and_profit" => "", "total" => "", "total_inventory_value" => "", "total_low_sell_quantity" => "", diff --git a/app/Language/vi/Attributes.php b/app/Language/vi/Attributes.php index 90bd2245e..ce35181e7 100644 --- a/app/Language/vi/Attributes.php +++ b/app/Language/vi/Attributes.php @@ -1,34 +1,36 @@ "Giá trị thuộc tính không thể chứa ':' hay '|'", - "confirm_delete" => "Bạn có chắc chắn muốn xóa (các) thuộc tính đã chọn không?", - "confirm_restore" => "Bạn có chắc chắn muốn khôi phục (các) thuộc tính đã chọn không?", - "definition_cannot_be_deleted" => "Không thể xóa (các) thuộc tính được chọn", - "definition_invalid_group" => "Nhóm đã chọn không tồn tại hoặc không hợp lệ.", - "definition_error_adding_updating" => "Thuộc tính {0} không thể thêm hoặc cập nhật. Vui lòng kiểm tra nhật ký lỗi.", - "definition_flags" => "Hiển thị thuộc tính", - "definition_group" => "Nhóm", - "definition_id" => "Mã số", - "definition_name" => "Thêm thuộc tính", - "definition_name_required" => "Tên thuộc tính là trường bắt buộc", - "definition_one_or_multiple" => "thuộc tính", - "definition_successful_adding" => "Bạn vừa mới thêm mục tin thành công", - "definition_successful_deleted" => "Bạn đã xóa thành công", - "definition_successful_updating" => "Bạn đã cập nhật thành công thuộc tính", - "definition_type" => "Loại thuộc tính", - "definition_type_required" => "Loại thuộc tính là trường bắt buộc", - "definition_unit" => "Đơn vị đo lường", - "definition_values" => "Giá trị thuộc tính", - "new" => "Thêm thuộc tính mới", - "no_attributes_to_display" => "Không có mặt hàng nào để hiển thị", - "receipt_visibility" => "Biên lai", - "show_in_items" => "Hiển thị trong các mặt hàng", - "show_in_items_visibility" => "Hàng hóa", - "show_in_receipt" => "Hiển thị trong biên lai", - "show_in_receivings" => "Hiển thị trong nhập hàng", - "show_in_receivings_visibility" => "Nhập hàng", - "show_in_sales" => "Hiển thị trong bán hàng", - "show_in_sales_visibility" => "Bán hàng", - "update" => "Cập nhật thuộc tính", + 'attribute_value_invalid_chars' => 'Giá trị thuộc tính không thể chứa \':\' hay \'|\'', + 'confirm_delete' => 'Bạn có chắc chắn muốn xóa (các) thuộc tính đã chọn không?', + 'confirm_restore' => 'Bạn có chắc chắn muốn khôi phục (các) thuộc tính đã chọn không?', + 'definition_cannot_be_deleted' => 'Không thể xóa (các) thuộc tính được chọn', + 'definition_invalid_group' => 'Nhóm đã chọn không tồn tại hoặc không hợp lệ.', + 'definition_error_adding_updating' => 'Thuộc tính {0} không thể thêm hoặc cập nhật. Vui lòng kiểm tra nhật ký lỗi.', + 'definition_flags' => 'Hiển thị thuộc tính', + 'definition_group' => 'Nhóm', + 'definition_id' => 'Mã số', + 'definition_name' => 'Thêm thuộc tính', + 'definition_name_required' => 'Tên thuộc tính là trường bắt buộc', + 'definition_one_or_multiple' => 'thuộc tính', + 'definition_successful_adding' => 'Bạn vừa mới thêm mục tin thành công', + 'definition_successful_deleted' => 'Bạn đã xóa thành công', + 'definition_successful_updating' => 'Bạn đã cập nhật thành công thuộc tính', + 'definition_type' => 'Loại thuộc tính', + 'definition_type_required' => 'Loại thuộc tính là trường bắt buộc', + 'definition_unit' => 'Đơn vị đo lường', + 'definition_values' => 'Giá trị thuộc tính', + 'new' => 'Thêm thuộc tính mới', + 'no_attributes_to_display' => 'Không có mặt hàng nào để hiển thị', + 'receipt_visibility' => 'Biên lai', + 'show_in_items' => 'Hiển thị trong các mặt hàng', + 'show_in_items_visibility' => 'Hàng hóa', + 'show_in_receipt' => 'Hiển thị trong biên lai', + 'show_in_receivings' => 'Hiển thị trong nhập hàng', + 'show_in_receivings_visibility' => 'Nhập hàng', + 'show_in_sales' => 'Hiển thị trong bán hàng', + 'show_in_sales_visibility' => 'Bán hàng', + 'show_in_search' => 'Hiển thị trong tìm kiếm', + 'show_in_search_visibility' => 'Tìm kiếm', + 'update' => 'Cập nhật thuộc tính', ]; diff --git a/app/Language/vi/Config.php b/app/Language/vi/Config.php index cb3678cad..c890022b3 100644 --- a/app/Language/vi/Config.php +++ b/app/Language/vi/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Tin nhắn văn bản đã lưu', 'msg_msg_placeholder' => 'Nếu bạn muốn dùng một mẫu thì lưu lại các tin nhắn SMS ở đây, nếu không thì để trống.', 'msg_pwd' => 'Mật khẩu SMS-API', - 'msg_pwd_required' => 'Mật khẩu SMS-API là trường bắt buộc', 'msg_src' => 'Mã số bộ gửi SMS-API', 'msg_src_required' => 'Mã số bộ gửi SMS-API là trường bắt buộc', 'msg_uid' => 'Tài khoản SMS-API', @@ -285,7 +284,7 @@ return [ 'right' => 'Phải', 'sales_invoice_format' => 'Định dạng Hóa đơn bán hàng', 'sales_quote_format' => 'Định dạng Báo giá bán hàng', - 'mailpath_invalid' => '', + 'mailpath_invalid' => 'Đường dẫn sendmail không hợp lệ. Chỉ cho phép chữ cái, số, dấu gạch ngang, dấu gạch dưới, dấu gạch chéo, dấu gạch chéo ngược, dấu hai chấm, dấu cách và dấu chấm.', 'saved_successfully' => 'Cấu hình được lưu thành công.', 'saved_unsuccessfully' => 'Gặp lỗi khi lưu cấu hình.', 'security_issue' => 'Cảnh báo về lỗ hổng bảo mật', diff --git a/app/Language/vi/Error.php b/app/Language/vi/Error.php index fae35e08c..89a13eb46 100644 --- a/app/Language/vi/Error.php +++ b/app/Language/vi/Error.php @@ -1,6 +1,14 @@ "Bạn không có thẩm quyền truy cập vào mô đun đó", - "unknown" => "Lỗi chưa biết", + 'no_permission_module' => 'Bạn không có thẩm quyền truy cập vào mô đun đó', + 'unable_to_create_env_file' => 'Không thể tạo {filePath} để thiết lập throttle.key', + 'unable_to_lock_file' => 'Không thể khóa {filePath}: {reason}', + 'unable_to_open_lock_file' => 'Không thể mở {filePath}: {reason}', + 'unable_to_persist_throttle_key' => 'Không thể lưu throttle.key vào {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => 'Không thể đọc {filePath} để thiết lập throttle.key', + 'unknown' => 'Lỗi chưa biết', ]; diff --git a/app/Language/vi/Reports.php b/app/Language/vi/Reports.php index 013453f70..fd99ba08e 100644 --- a/app/Language/vi/Reports.php +++ b/app/Language/vi/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "Tỷ suất thuế", "taxes" => "Thuế", "taxes_summary_report" => "Báo cáo tổng thể Thuế", + "toggle_cost_and_profit" => "", "total" => "Tổng cộng", "total_inventory_value" => "Giá trị tồn kho tổng cộng", "total_low_sell_quantity" => "Tổng số lượng hàng bán ít", diff --git a/app/Language/zh-Hans/Attributes.php b/app/Language/zh-Hans/Attributes.php index 883d91935..ff971d38c 100644 --- a/app/Language/zh-Hans/Attributes.php +++ b/app/Language/zh-Hans/Attributes.php @@ -1,34 +1,36 @@ "属性特征不能包含':' or '|'", - "confirm_delete" => "确定要删除所选属性吗", - "confirm_restore" => "您确定要还原所选属性吗?", - "definition_cannot_be_deleted" => "不能删除该特征属性", - "definition_invalid_group" => "所选组不存在或无效。", - "definition_error_adding_updating" => "无法添加或更新属性{0}。 请检查错误日志。", - "definition_flags" => "属性可见性", - "definition_group" => "组", - "definition_id" => "ID卡", - "definition_name" => "添加特征属性", - "definition_name_required" => "属性必须填写", - "definition_one_or_multiple" => "属性特征", - "definition_successful_adding" => "你成功添加物品", - "definition_successful_deleted" => "已经成功删除", - "definition_successful_updating" => "更新属性成功", - "definition_type" => "属性类别", - "definition_type_required" => "属性必填", - "definition_unit" => "计量单位", - "definition_values" => "属性特征值", - "new" => "新属性", - "no_attributes_to_display" => "没有物品需要显示", - "receipt_visibility" => "小票", - "show_in_items" => "在项目中显示", - "show_in_items_visibility" => "物品", - "show_in_receipt" => "在收据中显示", - "show_in_receivings" => "在收据中显示", - "show_in_receivings_visibility" => "收据", - "show_in_sales" => "在销售中显示", - "show_in_sales_visibility" => "销售", - "update" => "更新属性", + 'attribute_value_invalid_chars' => '属性特征不能包含\':\' or \'|\'', + 'confirm_delete' => '确定要删除所选属性吗', + 'confirm_restore' => '您确定要还原所选属性吗?', + 'definition_cannot_be_deleted' => '不能删除该特征属性', + 'definition_invalid_group' => '所选组不存在或无效。', + 'definition_error_adding_updating' => '无法添加或更新属性{0}。 请检查错误日志。', + 'definition_flags' => '属性可见性', + 'definition_group' => '组', + 'definition_id' => 'ID卡', + 'definition_name' => '添加特征属性', + 'definition_name_required' => '属性必须填写', + 'definition_one_or_multiple' => '属性特征', + 'definition_successful_adding' => '你成功添加物品', + 'definition_successful_deleted' => '已经成功删除', + 'definition_successful_updating' => '更新属性成功', + 'definition_type' => '属性类别', + 'definition_type_required' => '属性必填', + 'definition_unit' => '计量单位', + 'definition_values' => '属性特征值', + 'new' => '新属性', + 'no_attributes_to_display' => '没有物品需要显示', + 'receipt_visibility' => '小票', + 'show_in_items' => '在项目中显示', + 'show_in_items_visibility' => '物品', + 'show_in_receipt' => '在收据中显示', + 'show_in_receivings' => '在收据中显示', + 'show_in_receivings_visibility' => '收据', + 'show_in_sales' => '在销售中显示', + 'show_in_sales_visibility' => '销售', + 'show_in_search' => '在搜索中显示', + 'show_in_search_visibility' => '搜索', + 'update' => '更新属性', ]; diff --git a/app/Language/zh-Hans/Config.php b/app/Language/zh-Hans/Config.php index d75120a5e..21c77b8e5 100644 --- a/app/Language/zh-Hans/Config.php +++ b/app/Language/zh-Hans/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => 'Saved Text Message', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here. Otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API Password is a required field', 'msg_src' => 'SMS-API Sender ID', 'msg_src_required' => 'SMS-API Sender ID is a required field', 'msg_uid' => 'SMS-API Username', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => 'Sales Invoice Format', 'sales_quote_format' => '', - 'mailpath_invalid' => '', + 'mailpath_invalid' => '无效的 sendmail 路径。仅允许字母、数字、连字符、下划线、正斜杠、反斜杠、冒号、空格和点号。', 'saved_successfully' => '組態設置儲存成功', 'saved_unsuccessfully' => '組態設置儲存失敗', 'security_issue' => 'Security Vulnerability Warning', diff --git a/app/Language/zh-Hans/Error.php b/app/Language/zh-Hans/Error.php index edea68bc1..c8f2c04f8 100644 --- a/app/Language/zh-Hans/Error.php +++ b/app/Language/zh-Hans/Error.php @@ -1,6 +1,14 @@ "您沒有權限使用模組:", - "unknown" => "未知", + 'no_permission_module' => '您没有权限访问名为该模块', + 'unable_to_create_env_file' => '无法创建 {filePath} 以配置 throttle.key', + 'unable_to_lock_file' => '无法锁定 {filePath}:{reason}', + 'unable_to_open_lock_file' => '无法打开 {filePath}:{reason}', + 'unable_to_persist_throttle_key' => '无法将 throttle.key 保存到 {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => '无法读取 {filePath} 以配置 throttle.key', + 'unknown' => '未知', ]; diff --git a/app/Language/zh-Hans/Reports.php b/app/Language/zh-Hans/Reports.php index f98bda89f..dfd697361 100644 --- a/app/Language/zh-Hans/Reports.php +++ b/app/Language/zh-Hans/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "", "taxes" => "稅額", "taxes_summary_report" => "稅金摘要報告", + "toggle_cost_and_profit" => "", "total" => "總計", "total_inventory_value" => "Total Inventory Value", "total_low_sell_quantity" => "", diff --git a/app/Language/zh-Hant/Attributes.php b/app/Language/zh-Hant/Attributes.php index 66a3b19fa..723e709ce 100644 --- a/app/Language/zh-Hant/Attributes.php +++ b/app/Language/zh-Hant/Attributes.php @@ -1,34 +1,36 @@ "屬性設定值中不可含有「_」或「|」", - "confirm_delete" => "您確定要刪除此屬性?", - "confirm_restore" => "您確定要還原所選屬性嗎?", - "definition_cannot_be_deleted" => "無法刪除所選屬性", - "definition_invalid_group" => "所選擇的群組不存在或無效。", - "definition_error_adding_updating" => "無法添加或更新屬性 {0}。 請檢查錯誤日誌。", - "definition_flags" => "屬性可見性", - "definition_group" => "群組", - "definition_id" => "編號", - "definition_name" => "添加屬性", - "definition_name_required" => "屬性名稱必需填寫", - "definition_one_or_multiple" => "屬性", - "definition_successful_adding" => "您已成功添加項目", - "definition_successful_deleted" => "您已成功刪除", - "definition_successful_updating" => "您已成功更新屬性", - "definition_type" => "屬性類型", - "definition_type_required" => "屬性類型必需填寫", - "definition_unit" => "測量單位", - "definition_values" => "屬性值", - "new" => "新屬性", - "no_attributes_to_display" => "沒有項目可顯示", - "receipt_visibility" => "收據", - "show_in_items" => "在項目中顯示", - "show_in_items_visibility" => "物品", - "show_in_receipt" => "在收據中顯示", - "show_in_receivings" => "在收貨中顯示", - "show_in_receivings_visibility" => "收貨", - "show_in_sales" => "在銷售中顯示", - "show_in_sales_visibility" => "銷售", - "update" => "更新屬性", + 'attribute_value_invalid_chars' => '屬性設定值中不可含有「_」或「|」', + 'confirm_delete' => '您確定要刪除此屬性?', + 'confirm_restore' => '您確定要還原所選屬性嗎?', + 'definition_cannot_be_deleted' => '無法刪除所選屬性', + 'definition_invalid_group' => '所選擇的群組不存在或無效。', + 'definition_error_adding_updating' => '無法添加或更新屬性 {0}。 請檢查錯誤日誌。', + 'definition_flags' => '屬性可見性', + 'definition_group' => '群組', + 'definition_id' => '編號', + 'definition_name' => '添加屬性', + 'definition_name_required' => '屬性名稱必需填寫', + 'definition_one_or_multiple' => '屬性', + 'definition_successful_adding' => '您已成功添加項目', + 'definition_successful_deleted' => '您已成功刪除', + 'definition_successful_updating' => '您已成功更新屬性', + 'definition_type' => '屬性類型', + 'definition_type_required' => '屬性類型必需填寫', + 'definition_unit' => '測量單位', + 'definition_values' => '屬性值', + 'new' => '新屬性', + 'no_attributes_to_display' => '沒有項目可顯示', + 'receipt_visibility' => '收據', + 'show_in_items' => '在項目中顯示', + 'show_in_items_visibility' => '物品', + 'show_in_receipt' => '在收據中顯示', + 'show_in_receivings' => '在收貨中顯示', + 'show_in_receivings_visibility' => '收貨', + 'show_in_sales' => '在銷售中顯示', + 'show_in_sales_visibility' => '銷售', + 'show_in_search' => '在搜尋中顯示', + 'show_in_search_visibility' => '搜尋', + 'update' => '更新屬性', ]; diff --git a/app/Language/zh-Hant/Config.php b/app/Language/zh-Hant/Config.php index ecb6f570d..7cfce02d3 100644 --- a/app/Language/zh-Hant/Config.php +++ b/app/Language/zh-Hant/Config.php @@ -210,7 +210,6 @@ return [ 'msg_msg' => '保存的短信', 'msg_msg_placeholder' => 'If you wish to use a SMS template save your message here. Otherwise leave the box blank.', 'msg_pwd' => 'SMS-API Password', - 'msg_pwd_required' => 'SMS-API 密碼是必填字段', 'msg_src' => 'SMS-API 發件人 ID', 'msg_src_required' => 'SMS-API 發件人 ID 是必填字段', 'msg_uid' => 'SMS-API 用戶名', @@ -285,7 +284,7 @@ return [ 'right' => 'Right', 'sales_invoice_format' => '銷售發票格式', 'sales_quote_format' => '銷售報價格式', - 'mailpath_invalid' => '', + 'mailpath_invalid' => '無效的 sendmail 路徑。僅允許字母、數字、連字號、底線、正斜線、反斜線、冒號、空格和句點。', 'saved_successfully' => '組態設置儲存成功.', 'saved_unsuccessfully' => '組態設置儲存失敗.', 'security_issue' => '安全漏洞警告', diff --git a/app/Language/zh-Hant/Error.php b/app/Language/zh-Hant/Error.php index 78e324b5b..e0a9cff26 100644 --- a/app/Language/zh-Hant/Error.php +++ b/app/Language/zh-Hant/Error.php @@ -1,6 +1,14 @@ "您沒有權限使用模組", - "unknown" => "發生未知的錯誤", + 'no_permission_module' => '您沒有權限使用模組', + 'unable_to_create_env_file' => '無法建立 {filePath} 以設定 throttle.key', + 'unable_to_lock_file' => '無法鎖定 {filePath}:{reason}', + 'unable_to_open_lock_file' => '無法開啟 {filePath}:{reason}', + 'unable_to_persist_throttle_key' => '無法將 throttle.key 儲存至 {filePath}', + 'unable_to_persist_encryption_key' => '', + 'encryption_key_not_provisioned' => '', + 'throttle_key_not_provisioned' => '', + 'unable_to_read_env_file' => '無法讀取 {filePath} 以設定 throttle.key', + 'unknown' => '發生未知的錯誤', ]; diff --git a/app/Language/zh-Hant/Reports.php b/app/Language/zh-Hant/Reports.php index 3df510f36..da361338e 100644 --- a/app/Language/zh-Hant/Reports.php +++ b/app/Language/zh-Hant/Reports.php @@ -128,6 +128,7 @@ return [ "tax_rate" => "稅率", "taxes" => "稅額", "taxes_summary_report" => "稅金摘要報告", + "toggle_cost_and_profit" => "", "total" => "總計", "total_inventory_value" => "總庫存價值", "total_low_sell_quantity" => "總低銷售量", diff --git a/app/Libraries/CI3SecretConverter.php b/app/Libraries/CI3SecretConverter.php new file mode 100644 index 000000000..545cebe7f --- /dev/null +++ b/app/Libraries/CI3SecretConverter.php @@ -0,0 +1,173 @@ + CI4 encrypted-secret migration. + * + * CI3 encrypted its config secrets (SMTP password, Mailchimp API key, etc.) + * with AES-128-CBC + `encryptKeyInfo='encryption'` + `rawData=false` + + * `authKeyInfo='authentication'`. CI4 defaults to AES-256-CTR, and the CI4 + * "previous keys" fallback cannot bridge a *cipher* change -- it only rotates + * keys within a single cipher. So legacy rows must be decrypted with the old + * cipher and re-encrypted with the new one. + * + * This class centralises both ciphers so the interactive UI migration + * (ConvertToCI4) and the docker startup command (env:provision) share a single + * implementation. It performs no persistence of its own except saveAll(); key + * rotation is left to the caller (rotateEncryptionKey in security_helper). + */ +class CI3SecretConverter +{ + /** + * Legacy CI3 secret keys stored in the ospos_app_config table. + * + * @var list + */ + public const LEGACY_KEYS = [ + 'clcdesq_api_key', + 'clcdesq_api_url', + 'mailchimp_api_key', + 'mailchimp_list_id', + 'smtp_pass', + ]; + + /** + * @var Appconfig|null + */ + private ?Appconfig $model; + + public function __construct(?Appconfig $model = null) + { + $this->model = $model; + } + + /** + * Decrypts all legacy CI3 secrets into plaintext using the CI3 cipher and + * the supplied key. Empty/missing rows become ''. + * + * @param string $key the CI3-era encryption key + * @return array + */ + public function decryptAll(string $key): array + { + $encrypter = Services::encrypter($this->ci3Config($key)); + $appConfig = $this->resolveModel(); + + $result = []; + foreach (self::LEGACY_KEYS as $col) { + $value = (string) $appConfig->get_value($col); + $result[$col] = $value === '' ? '' : $encrypter->decrypt($value); + } + + return $result; + } + + /** + * Encrypts plaintext values under the current CI4 cipher (no persistence). + * + * @param array $plain + * @return array + */ + public function encryptAll(array $plain): array + { + $encrypter = Services::encrypter(); + + $result = []; + foreach ($plain as $col => $value) { + $value = (string) $value; + $result[$col] = $value === '' ? '' : $encrypter->encrypt($value); + } + + return $result; + } + + /** + * Decrypts CI4-cipher values back to plaintext. Used to verify a round + * trip before persisting the converted result. + * + * @param array $encrypted + * @return array + */ + public function verifyAll(array $encrypted): array + { + $encrypter = Services::encrypter(); + + $result = []; + foreach ($encrypted as $col => $value) { + $value = (string) $value; + $result[$col] = $value === '' ? '' : $encrypter->decrypt($value); + } + + return $result; + } + + /** + * Persists already-encrypted values to the ospos_app_config table. + * + * @param array $encrypted + * @return bool + * @throws RuntimeException + */ + public function saveAll(array $encrypted): bool + { + if (!$this->resolveModel()->batch_save($encrypted)) { + throw new RuntimeException('Failed to save converted encryption data. Check logs for details.'); + } + + return true; + } + + /** + * True when at least one legacy secret decrypts to a non-empty plaintext + * value with $oldKey. Used to decide whether a conversion is necessary. + */ + public function hasLegacyData(string $oldKey): bool + { + foreach ($this->decryptAll($oldKey) as $value) { + if ((string) $value !== '') { + return true; + } + } + + return false; + } + + /** + * Build a Config\Encryption instance matching the CI3 cipher settings so + * Services::encrypter() can construct a handler with AES-128-CBC. + * + * @return EncryptionConfig + */ + private function ci3Config(string $key): EncryptionConfig + { + $config = new EncryptionConfig(); + $config->driver = 'OpenSSL'; + $config->digest = 'SHA512'; + $config->key = $key; + $config->cipher = 'AES-128-CBC'; + $config->rawData = false; + $config->encryptKeyInfo = 'encryption'; + $config->authKeyInfo = 'authentication'; + $config->previousKeys = []; + + return $config; + } + + /** + * @return Appconfig + */ + private function resolveModel(): Appconfig + { + if ($this->model === null) { + $this->model = model(Appconfig::class); + } + + return $this->model; + } +} diff --git a/app/Models/Attribute.php b/app/Models/Attribute.php index 5b43a9bf0..79a021ffd 100644 --- a/app/Models/Attribute.php +++ b/app/Models/Attribute.php @@ -38,9 +38,10 @@ class Attribute extends Model 'attribute_decimal' ]; - public const SHOW_IN_ITEMS = 1; // TODO: These need to be moved to constants.php + public const SHOW_IN_ITEMS = 1; public const SHOW_IN_SALES = 2; public const SHOW_IN_RECEIVINGS = 4; + public const SHOW_IN_SEARCH = 8; public function deleteDropdownAttributeValue(string $attribute_value, int $definition_id): bool { $attribute_id = $this->getAttributeIdByValue($attribute_value); diff --git a/app/Models/Item.php b/app/Models/Item.php index 1268839a3..3eba73183 100644 --- a/app/Models/Item.php +++ b/app/Models/Item.php @@ -2,9 +2,11 @@ namespace App\Models; +use CodeIgniter\Database\RawSql; use CodeIgniter\Database\ResultInterface; use CodeIgniter\Model; use Config\OSPOS; +use DateTime; use ReflectionException; use stdClass; @@ -38,6 +40,7 @@ class Item extends Model 'allow_alt_description', 'is_serialized' ]; + protected $table = 'items'; protected $primaryKey = 'item_id'; protected $useAutoIncrement = true; @@ -65,7 +68,6 @@ class Item extends Model 'hsn_code' ]; - /** * Determines if a given item_id is an item */ @@ -140,16 +142,183 @@ class Item extends Model } /** - * Perform a search on items + * Parse search string for attribute-specific queries + * Supports syntax like "color: blue size: large" or "color:blue AND size:large" + * + * @param string $search The raw search string + * @return array{terms: array, attributes: array} Parsed terms and attribute queries */ - public function search(string $search, array $filters, ?int $rows = 0, ?int $limit_from = 0, ?string $sort = 'items.name', ?string $order = 'asc', ?bool $count_only = false) + public function parseAttributeSearch(string $search): array + { + $result = [ + 'terms' => [], + 'attributes' => [] + ]; + + if ($search === '') { + return $result; + } + + $pattern = '/([[:alpha:]][[:alnum:] _-]*?)\s*:\s*([0-9]+,[0-9]+|[^\s,]+)(?:\s*,\s*|\s+(?:AND|OR)\s+)?/iu'; + $remaining = preg_replace($pattern, '', $search); + + if (preg_match_all($pattern, $search, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $attrName = strtolower(trim($match[1])); + $attrValue = trim($match[2]); + $result['attributes'][$attrName][] = $attrValue; + } + } + + $remaining = trim(preg_replace('/\s+/', ' ', $remaining)); + if ($remaining !== '') { + $result['terms'][] = $remaining; + } + + return $result; + } + + /** + * Joins rather than correlated EXISTS per name: EXISTS made MySQL materialize one + * subquery as a full attribute_links scan instead of using attribute_links_uq2 (~400ms vs ~150ms). + * + * $definitionInfo must be resolved by the caller before $builder exists - a query here + * would reset CodeIgniter's table-alias tracking and corrupt $builder's "items" alias. + * + * @param array $definitionInfo definition_id => info, from getDefinitionsByFlags(..., true) + * @param array $parsedAttributes Attribute name => list of search values + * @param int[] $allowedDefinitionIds Definition ids this search is scoped to + * @return string[] Attribute names that resolved to a known, allowed definition and were joined into $builder + */ + private function applyNamedAttributeSearch($builder, array $definitionInfo, array $parsedAttributes, array $allowedDefinitionIds): array + { + $definitionIdByName = []; + foreach ($definitionInfo as $id => $info) { + $name = is_array($info) ? $info['name'] : $info; + $definitionIdByName[strtolower($name)] = (int) $id; + } + + $consumedNames = []; + $index = 0; + foreach ($parsedAttributes as $attrName => $values) { + if (!isset($definitionIdByName[$attrName])) { + continue; + } + + $definitionId = $definitionIdByName[$attrName]; + + if (!in_array($definitionId, $allowedDefinitionIds, true)) { + continue; + } + + $consumedNames[] = $attrName; + + $defType = is_array($definitionInfo[$definitionId]) ? ($definitionInfo[$definitionId]['type'] ?? TEXT) : TEXT; + + $linksAlias = "named_attr_links_{$index}"; + $valuesAlias = "named_attr_values_{$index}"; + $index++; + + $builder->join( + "attribute_links AS {$linksAlias}", + "{$linksAlias}.item_id = items.item_id AND {$linksAlias}.definition_id = {$definitionId} AND {$linksAlias}.receiving_id IS NULL AND {$linksAlias}.sale_id IS NULL" + ); + $builder->join( + "attribute_values AS {$valuesAlias}", + "{$valuesAlias}.attribute_id = {$linksAlias}.attribute_id" + ); + + $builder->groupStart(); + $matched = false; + foreach ($values as $value) { + if ($defType === DECIMAL) { + $parsedValue = parse_decimals($value); + if ($parsedValue === false) { + continue; + } + $builder->orWhere("{$valuesAlias}.attribute_decimal", $parsedValue); + $matched = true; + } elseif ($defType === DATE) { + $config = config(OSPOS::class)->settings; + $date = DateTime::createFromFormat($config['dateformat'], $value); + if ($date === false) { + continue; + } + $builder->orWhere("{$valuesAlias}.attribute_date", $date->format('Y-m-d')); + $matched = true; + } else { + $builder->orWhere("{$valuesAlias}.attribute_value", $value); + $matched = true; + } + } + $builder->groupEnd(); + + if (!$matched) { + $builder->where('1 = 0', null, false); + } + } + + return $consumedNames; + } + + /** + * Get attribute definition ID from column name for sorting + * + * @param string $sortColumn The sort column name + * @return int|null The definition ID or null if not an attribute column + */ + private function getAttributeSortDefinitionId(string $sortColumn): ?int + { + if (!ctype_digit($sortColumn)) { + return null; + } + + return (int) $sortColumn; + } + + /** + * Left-joins the attribute tables needed to sort by a given attribute definition, + * and applies the order-by using the type-appropriate value column. + * + * $definitionInfo must be resolved by the caller before any joins are added to $builder — + * see the note on applyNamedAttributeSearch() for why a query here would corrupt $builder. + * + * @param array $definitionInfo definition_id => info, from getDefinitionsByFlags(..., true) + */ + private function applyAttributeSort($builder, array $definitionInfo, int $sortDefinitionId, string $order): void + { + $sortAlias = "sort_attr_{$sortDefinitionId}"; + $builder->join("attribute_links AS {$sortAlias}", "{$sortAlias}.item_id = items.item_id AND {$sortAlias}.definition_id = {$sortDefinitionId} AND {$sortAlias}.sale_id IS NULL AND {$sortAlias}.receiving_id IS NULL", 'left'); + $builder->join("attribute_values AS {$sortAlias}_val", "{$sortAlias}_val.attribute_id = {$sortAlias}.attribute_id", 'left'); + + $sortColumn = "{$sortAlias}_val.attribute_value"; + + if (isset($definitionInfo[$sortDefinitionId])) { + $defType = is_array($definitionInfo[$sortDefinitionId]) ? ($definitionInfo[$sortDefinitionId]['type'] ?? TEXT) : TEXT; + if ($defType === DECIMAL) { + $sortColumn = "{$sortAlias}_val.attribute_decimal"; + } elseif ($defType === DATE) { + $sortColumn = "{$sortAlias}_val.attribute_date"; + } + } + + $builder->orderBy("MAX($sortColumn)", $order); + } + + /** + * Perform a search on items + * + * Resolves qualifying item_ids first (Phase A), then joins the expensive display-only + * tables scoped to just those ids (Phase B) instead of the whole matching set. + */ + public function search(string $search, array $filters, ?int $rows = 0, ?int $limitFrom = 0, ?string $sort = 'items.name', ?string $order = 'asc', ?bool $countOnly = false) { // Set default values if ($rows == null) { $rows = 0; } - if ($limit_from == null) { - $limit_from = 0; + if ($limitFrom == null) { + $limitFrom = 0; } if ($sort == null) { $sort = 'items.name'; @@ -157,53 +326,196 @@ class Item extends Model if ($order == null) { $order = 'asc'; } - if ($count_only == null) { - $count_only = false; + if ($countOnly == null) { + $countOnly = false; } $config = config(OSPOS::class)->settings; - $builder = $this->db->table('items AS items'); // TODO: I'm not sure if it's needed to write items AS items... I think you can just get away with items + + $dateFormatEnabled = empty($config['date_or_time_format']); + $hasTransDateRange = !empty($filters['start_date']) && !empty($filters['end_date']); + $rangeStart = $hasTransDateRange ? ($dateFormatEnabled ? $filters['start_date'] : rawurldecode($filters['start_date'])) : null; + $rangeEnd = $hasTransDateRange ? ($dateFormatEnabled ? $filters['end_date'] : rawurldecode($filters['end_date'])) : null; + $applyTransDateRange = function ($builder) use ($hasTransDateRange, $dateFormatEnabled, $rangeStart, $rangeEnd) { + if (!$hasTransDateRange) { + return; + } + $column = $dateFormatEnabled ? 'DATE_FORMAT(trans_date, "%Y-%m-%d")' : 'trans_date'; + $builder->groupStart(); + $builder->where("$column >=", $rangeStart); + $builder->where("$column <=", $rangeEnd); + $builder->groupEnd(); + }; + + $definitionIds = array_map('intval', $filters['definition_ids']); + $attributesEnabled = count($filters['definition_ids']) > 0; + $customAttributeSearch = $attributesEnabled && $filters['search_custom'] && !empty($search); + + // Resolved before $idBuilder exists - see applyNamedAttributeSearch() for why. + $attribute = model(Attribute::class); + $definitionInfo = $attribute->getDefinitionsByFlags(Attribute::SHOW_IN_ITEMS | Attribute::SHOW_IN_SEARCH, true); + + if ($attributesEnabled) { + $this->db->simpleQuery('SET SESSION group_concat_max_len=49152'); + } + + $idBuilder = $this->db->table('items AS items'); + + if ($customAttributeSearch) { + // Matching is per attribute row (WHERE, pre-GROUP BY), not against a GROUP_CONCAT + // blob (HAVING, post-GROUP BY), so a match can't bleed across definitions. + $idBuilder->select('items.item_id'); + $idBuilder->join('attribute_links', 'attribute_links.item_id = items.item_id AND attribute_links.receiving_id IS NULL AND attribute_links.sale_id IS NULL AND definition_id IN (' . implode(',', $definitionIds) . ')', 'left'); + $idBuilder->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id', 'left'); + } else { + $idBuilder->select('items.item_id'); + } + + $idBuilder->join('suppliers AS suppliers', 'suppliers.person_id = items.supplier_id', 'left'); + $idBuilder->join('inventory AS inventory', 'inventory.trans_items = items.item_id'); + + if ($filters['stock_location_id'] > -1) { + $idBuilder->join('item_quantities AS item_quantities', 'item_quantities.item_id = items.item_id'); + $idBuilder->where('location_id', $filters['stock_location_id']); + } + + $applyTransDateRange($idBuilder); + + $parsedAttributeSearch = $customAttributeSearch ? $this->parseAttributeSearch($search) : null; + $freeTextSearch = $search; + + if ($parsedAttributeSearch !== null && !empty($parsedAttributeSearch['attributes'])) { + $consumedNames = $this->applyNamedAttributeSearch($idBuilder, $definitionInfo, $parsedAttributeSearch['attributes'], $definitionIds); + + $unconsumedTerms = $parsedAttributeSearch['terms']; + foreach ($parsedAttributeSearch['attributes'] as $attrName => $values) { + if (in_array($attrName, $consumedNames, true)) { + continue; + } + foreach ($values as $value) { + $unconsumedTerms[] = $value; + } + } + + $freeTextSearch = implode(' ', $unconsumedTerms); + } + + if (!empty($freeTextSearch)) { + if ($customAttributeSearch) { + $format = $this->db->escape(dateformat_mysql()); + $attributeValuesTable = $this->db->prefixTable('attribute_values'); + $idBuilder->groupStart(); + $idBuilder->like('attribute_values.attribute_value', $freeTextSearch); + $idBuilder->orLike(new RawSql("DATE_FORMAT($attributeValuesTable.attribute_date, $format)"), $freeTextSearch); + $idBuilder->orLike('attribute_values.attribute_decimal', $freeTextSearch); + $idBuilder->groupEnd(); + } else { + $idBuilder->groupStart(); + $idBuilder->like('name', $search); + $idBuilder->orLike('item_number', $search); + $idBuilder->orLike('items.item_id', $search); + $idBuilder->orLike('company_name', $search); + $idBuilder->orLike('items.category', $search); + $idBuilder->groupEnd(); + } + } + + $idBuilder->where('items.deleted', $filters['is_deleted']); + + if ($filters['empty_upc']) { + $idBuilder->where('item_number', null); + } + if ($filters['low_inventory'] && $filters['stock_location_id'] > -1) { + $idBuilder->where('item_quantities.quantity <=', new RawSql('items.reorder_level')); + } + if ($filters['is_serialized']) { + $idBuilder->where('is_serialized', 1); + } + if ($filters['no_description']) { + $idBuilder->where('items.description', ''); + } + if ($filters['temporary']) { + $idBuilder->where('items.item_type', ITEM_TEMP); + } else { + $nonTemp = [ITEM, ITEM_KIT, ITEM_AMOUNT_ENTRY]; + $idBuilder->whereIn('items.item_type', $nonTemp); + } + + // Avoid duplicated entries with same name because of inventory reporting multiple changes on the same item in the same date range + $idBuilder->groupBy('items.item_id'); // get_found_rows case - if ($count_only) { - $builder->select('COUNT(DISTINCT items.item_id) AS count'); + if ($countOnly) { + return $idBuilder->countAllResults(); + } + + // Order by name of item by default + $sortDefinitionId = $this->getAttributeSortDefinitionId($sort); + if ($sort === 'quantity' && $filters['stock_location_id'] <= -1) { + $itemQuantitiesTable = $this->db->prefixTable('item_quantities'); + $idBuilder->join( + "(SELECT item_id, SUM(quantity) AS total_quantity FROM $itemQuantitiesTable GROUP BY item_id) AS item_quantity_totals", + 'item_quantity_totals.item_id = items.item_id', + 'left' + ); + $idBuilder->orderBy('MAX(item_quantity_totals.total_quantity)', $order); + } elseif ($sortDefinitionId !== null) { + $this->applyAttributeSort($idBuilder, $definitionInfo, $sortDefinitionId, $order); } else { - $builder->select('MAX(items.item_id) AS item_id'); - $builder->select('MAX(items.name) AS name'); - $builder->select('MAX(items.category) AS category'); - $builder->select('MAX(items.supplier_id) AS supplier_id'); - $builder->select('MAX(items.item_number) AS item_number'); - $builder->select('MAX(items.description) AS description'); - $builder->select('MAX(items.cost_price) AS cost_price'); - $builder->select('MAX(items.unit_price) AS unit_price'); - $builder->select('MAX(items.reorder_level) AS reorder_level'); - $builder->select('MAX(items.receiving_quantity) AS receiving_quantity'); - $builder->select('MAX(items.pic_filename) AS pic_filename'); - $builder->select('MAX(items.allow_alt_description) AS allow_alt_description'); - $builder->select('MAX(items.is_serialized) AS is_serialized'); - $builder->select('MAX(items.pack_name) AS pack_name'); - $builder->select('MAX(items.tax_category_id) AS tax_category_id'); - $builder->select('MAX(items.deleted) AS deleted'); + $idBuilder->orderBy($sort, $order); + } - $builder->select('MAX(suppliers.person_id) AS person_id'); - $builder->select('MAX(suppliers.company_name) AS company_name'); - $builder->select('MAX(suppliers.agency_name) AS agency_name'); - $builder->select('MAX(suppliers.account_number) AS account_number'); - $builder->select('MAX(suppliers.deleted) AS deleted'); + if ($rows > 0) { + $idBuilder->limit($rows, $limitFrom); + } - $builder->select('MAX(inventory.trans_id) AS trans_id'); - $builder->select('MAX(inventory.trans_items) AS trans_items'); - $builder->select('MAX(inventory.trans_user) AS trans_user'); - $builder->select('MAX(inventory.trans_date) AS trans_date'); - $builder->select('MAX(inventory.trans_comment) AS trans_comment'); - $builder->select('MAX(inventory.trans_location) AS trans_location'); - $builder->select('MAX(inventory.trans_inventory) AS trans_inventory'); + $itemIds = array_column($idBuilder->get()->getResultArray(), 'item_id'); - if ($filters['stock_location_id'] > -1) { - $builder->select('MAX(item_quantities.item_id) AS qty_item_id'); - $builder->select('MAX(item_quantities.location_id) AS location_id'); - $builder->select('MAX(item_quantities.quantity) AS quantity'); - } + if (empty($itemIds)) { + return $this->db->table('items AS items')->where('1 = 0')->get(); + } + + $builder = $this->db->table('items AS items'); + + $builder->select('MAX(items.item_id) AS item_id'); + $builder->select('MAX(items.name) AS name'); + $builder->select('MAX(items.category) AS category'); + $builder->select('MAX(items.supplier_id) AS supplier_id'); + $builder->select('MAX(items.item_number) AS item_number'); + $builder->select('MAX(items.description) AS description'); + $builder->select('MAX(items.cost_price) AS cost_price'); + $builder->select('MAX(items.unit_price) AS unit_price'); + $builder->select('MAX(items.reorder_level) AS reorder_level'); + $builder->select('MAX(items.receiving_quantity) AS receiving_quantity'); + $builder->select('MAX(items.pic_filename) AS pic_filename'); + $builder->select('MAX(items.allow_alt_description) AS allow_alt_description'); + $builder->select('MAX(items.is_serialized) AS is_serialized'); + $builder->select('MAX(items.pack_name) AS pack_name'); + $builder->select('MAX(items.tax_category_id) AS tax_category_id'); + $builder->select('MAX(items.deleted) AS deleted'); + + $builder->select('MAX(suppliers.person_id) AS person_id'); + $builder->select('MAX(suppliers.company_name) AS company_name'); + $builder->select('MAX(suppliers.agency_name) AS agency_name'); + $builder->select('MAX(suppliers.account_number) AS account_number'); + $builder->select('MAX(suppliers.deleted) AS supplier_deleted'); + + $builder->select('MAX(inventory.trans_id) AS trans_id'); + $builder->select('MAX(inventory.trans_items) AS trans_items'); + $builder->select('MAX(inventory.trans_user) AS trans_user'); + $builder->select('MAX(inventory.trans_date) AS trans_date'); + $builder->select('MAX(inventory.trans_comment) AS trans_comment'); + $builder->select('MAX(inventory.trans_location) AS trans_location'); + $builder->select('MAX(inventory.trans_inventory) AS trans_inventory'); + + $sortByQuantityAllLocations = $sort === 'quantity' && $filters['stock_location_id'] <= -1; + + if ($filters['stock_location_id'] > -1) { + $builder->select('MAX(item_quantities.item_id) AS qty_item_id'); + $builder->select('MAX(item_quantities.location_id) AS location_id'); + $builder->select('MAX(item_quantities.quantity) AS quantity'); + } elseif ($sortByQuantityAllLocations) { + $builder->select('MAX(item_quantity_totals.total_quantity) AS quantity'); } $builder->join('suppliers AS suppliers', 'suppliers.person_id = items.supplier_id', 'left'); @@ -212,75 +524,39 @@ class Item extends Model if ($filters['stock_location_id'] > -1) { $builder->join('item_quantities AS item_quantities', 'item_quantities.item_id = items.item_id'); $builder->where('location_id', $filters['stock_location_id']); + } elseif ($sortByQuantityAllLocations) { + $itemQuantitiesTable = $this->db->prefixTable('item_quantities'); + $builder->join( + "(SELECT item_id, SUM(quantity) AS total_quantity FROM $itemQuantitiesTable GROUP BY item_id) AS item_quantity_totals", + 'item_quantity_totals.item_id = items.item_id', + 'left' + ); } - $where = empty($config['date_or_time_format']) - ? 'DATE_FORMAT(trans_date, "%Y-%m-%d") BETWEEN ' . $this->db->escape($filters['start_date']) . ' AND ' . $this->db->escape($filters['end_date']) - : 'trans_date BETWEEN ' . $this->db->escape(rawurldecode($filters['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($filters['end_date'])); - $builder->where($where); + $applyTransDateRange($builder); - $attributes_enabled = count($filters['definition_ids']) > 0; - - if (!empty($search)) { - if ($attributes_enabled && $filters['search_custom']) { - $builder->havingLike('attribute_values', $search); - $builder->orHavingLike('attribute_dtvalues', $search); - $builder->orHavingLike('attribute_dvalues', $search); - } else { - $builder->groupStart(); - $builder->like('name', $search); - $builder->orLike('item_number', $search); - $builder->orLike('items.item_id', $search); - $builder->orLike('company_name', $search); - $builder->orLike('items.category', $search); - $builder->groupEnd(); - } - } - - if ($attributes_enabled) { + if ($attributesEnabled) { $format = $this->db->escape(dateformat_mysql()); - $this->db->simpleQuery('SET SESSION group_concat_max_len=49152'); - $builder->select('GROUP_CONCAT(DISTINCT CONCAT_WS(\'_\', definition_id, attribute_value) ORDER BY definition_id SEPARATOR \'|\') AS attribute_values'); - $builder->select("GROUP_CONCAT(DISTINCT CONCAT_WS('_', definition_id, DATE_FORMAT(attribute_date, $format)) SEPARATOR '|') AS attribute_dtvalues"); - $builder->select('GROUP_CONCAT(DISTINCT CONCAT_WS(\'_\', definition_id, attribute_decimal) SEPARATOR \'|\') AS attribute_dvalues'); - $builder->join('attribute_links', 'attribute_links.item_id = items.item_id AND attribute_links.receiving_id IS NULL AND attribute_links.sale_id IS NULL AND definition_id IN (' . implode(',', $filters['definition_ids']) . ')', 'left'); + $attributeLinksTable = $this->db->prefixTable('attribute_links'); + $attributeValuesTable = $this->db->prefixTable('attribute_values'); + $builder->select("GROUP_CONCAT(DISTINCT CONCAT_WS('_', $attributeLinksTable.definition_id, $attributeValuesTable.attribute_value) ORDER BY $attributeLinksTable.definition_id SEPARATOR '|') AS attribute_values"); + $builder->select("GROUP_CONCAT(DISTINCT CONCAT_WS('_', $attributeLinksTable.definition_id, DATE_FORMAT($attributeValuesTable.attribute_date, $format)) SEPARATOR '|') AS attribute_dtvalues"); + $builder->select("GROUP_CONCAT(DISTINCT CONCAT_WS('_', $attributeLinksTable.definition_id, $attributeValuesTable.attribute_decimal) SEPARATOR '|') AS attribute_dvalues"); + $builder->join('attribute_links', 'attribute_links.item_id = items.item_id AND attribute_links.receiving_id IS NULL AND attribute_links.sale_id IS NULL AND attribute_links.definition_id IN (' . implode(',', $definitionIds) . ')', 'left'); $builder->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id', 'left'); } - $builder->where('items.deleted', $filters['is_deleted']); + $builder->whereIn('items.item_id', $itemIds); - if ($filters['empty_upc']) { - $builder->where('item_number', null); - } - if ($filters['low_inventory']) { - $builder->where('quantity <=', 'reorder_level'); - } - if ($filters['is_serialized']) { - $builder->where('is_serialized', 1); - } - if ($filters['no_description']) { - $builder->where('items.description', ''); - } - if ($filters['temporary']) { - $builder->where('items.item_type', ITEM_TEMP); - } else { - $non_temp = [ITEM, ITEM_KIT, ITEM_AMOUNT_ENTRY]; - $builder->whereIn('items.item_type', $non_temp); - } - - // get_found_rows case - if ($count_only) { - return $builder->get()->getRow()->count; - } - - // Avoid duplicated entries with same name because of inventory reporting multiple changes on the same item in the same date range $builder->groupBy('items.item_id'); - // Order by name of item by default - $builder->orderBy($sort, $order); - - if ($rows > 0) { - $builder->limit($rows, $limit_from); + // Re-apply order: WHERE...IN + GROUP BY do not preserve Phase A's row order + if ($sortByQuantityAllLocations) { + $builder->orderBy('MAX(item_quantity_totals.total_quantity)', $order); + } elseif ($sortDefinitionId !== null) { + $this->applyAttributeSort($builder, $definitionInfo, $sortDefinitionId, $order); + } else { + $builder->orderBy($sort, $order); } return $builder->get(); @@ -529,7 +805,7 @@ class Item extends Model */ public function updateMultiple(array $itemData, string $itemIds): bool { - // Query Builder bypasses $allowedFields, so the whitelist is enforced here (GHSA-49mq-h2g4-grr9) + // Query Builder bypasses $allowedFields, so the whitelist is enforced here $itemData = array_intersect_key($itemData, array_flip(self::ALLOWED_BULK_EDIT_FIELDS)); if (empty($itemData)) { diff --git a/app/Models/Item_taxes.php b/app/Models/Item_taxes.php index 08b8fb845..bfb07b36a 100644 --- a/app/Models/Item_taxes.php +++ b/app/Models/Item_taxes.php @@ -30,6 +30,26 @@ class Item_taxes extends Model return $builder->get()->getResultArray(); } + /** + * @return array item_id => array of taxes for that item + */ + public function getInfoMultiple(array $itemIds): array + { + if (empty($itemIds)) { + return []; + } + + $builder = $this->db->table('items_taxes'); + $builder->whereIn('item_id', $itemIds); + + $taxesByItemId = []; + foreach ($builder->get()->getResultArray() as $tax) { + $taxesByItemId[$tax['item_id']][] = $tax; + } + + return $taxesByItemId; + } + /** * Inserts or updates an item's taxes */ diff --git a/app/Views/attributes/item.php b/app/Views/attributes/item.php index b807dc10c..75dff9927 100644 --- a/app/Views/attributes/item.php +++ b/app/Views/attributes/item.php @@ -12,7 +12,7 @@
'definition_name', - 'options' => $definition_names, + 'options' => esc($definition_names), 'selected' => -1, 'class' => 'form-control', 'id' => 'definition_name' @@ -45,7 +45,7 @@ $selected_value = $definition_value['selected_value']; echo form_dropdown([ 'name' => "attribute_links[$definition_id]", - 'options' => $definition_value['values'], + 'options' => esc($definition_value['values']), 'selected' => $selected_value, 'class' => 'form-control', 'data-definition-id' => $definition_id diff --git a/app/Views/configs/email_config.php b/app/Views/configs/email_config.php index f3563ca56..46d0f26b1 100644 --- a/app/Views/configs/email_config.php +++ b/app/Views/configs/email_config.php @@ -1,6 +1,7 @@ @@ -117,12 +118,12 @@ - 'smtp_pass', - 'id' => 'smtp_pass', - 'class' => 'form-control input-sm', - 'value' => $config['smtp_pass'] - ]) ?> +
@@ -146,8 +147,9 @@ $('#mailpath').prop('disabled', false); $('#smtp_host, #smtp_user, #smtp_pass, #smtp_port, #smtp_timeout, #smtp_crypto').prop('disabled', true); } else if ($('#protocol').val() == 'smtp') { - $('#smtp_host, #smtp_user, #smtp_pass, #smtp_port, #smtp_timeout, #smtp_crypto').prop('disabled', false); + $('#smtp_host, #smtp_user, #smtp_port, #smtp_timeout, #smtp_crypto').prop('disabled', false); $('#mailpath').prop('disabled', true); + $('#smtp_pass').prop('disabled', false); } else { $('#mailpath, #smtp_host, #smtp_user, #smtp_pass, #smtp_port, #smtp_timeout, #smtp_crypto').prop('disabled', true); } diff --git a/app/Views/configs/integrations_config.php b/app/Views/configs/integrations_config.php index ea5881643..7e8f24909 100644 --- a/app/Views/configs/integrations_config.php +++ b/app/Views/configs/integrations_config.php @@ -20,12 +20,13 @@ - 'mailchimp_api_key', - 'id' => 'mailchimp_api_key', - 'class' => 'form-control input-sm', - 'value' => $mailchimp['api_key'] - ]) ?> + +
diff --git a/app/Views/configs/message_config.php b/app/Views/configs/message_config.php index 3ddae26b6..190628789 100644 --- a/app/Views/configs/message_config.php +++ b/app/Views/configs/message_config.php @@ -1,6 +1,7 @@ @@ -29,18 +30,18 @@
- 'control-label col-xs-2 required']) ?> + 'control-label col-xs-2']) ?>
- 'msg_pwd', - 'id' => 'msg_pwd', - 'class' => 'form-control input-sm required', - 'value' => $config['msg_pwd'] - ]) ?> +
@@ -95,13 +96,11 @@ rules: { msg_uid: "required", - msg_pwd: "required", msg_src: "required" }, messages: { msg_uid: "", - msg_pwd: "", msg_src: "" } })); diff --git a/app/Views/expenses/manage.php b/app/Views/expenses/manage.php index 945429d7d..ecdba7450 100644 --- a/app/Views/expenses/manage.php +++ b/app/Views/expenses/manage.php @@ -14,12 +14,18 @@