mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-05-26 09:15:28 -04:00
Compare commits
6 Commits
WebShells-
...
refactor-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a0f0c7f4c | ||
|
|
7a7ee4a5f3 | ||
|
|
418dbc69e8 | ||
|
|
e177099be1 | ||
|
|
513cccad69 | ||
|
|
91137a43a2 |
@@ -16,9 +16,6 @@ CI_ENVIRONMENT = production
|
||||
# Configure with comma-separated list of domains/subdomains:
|
||||
# app.allowedHostnames = 'yourdomain.com,www.yourdomain.com'
|
||||
#
|
||||
# Or via environment variable (useful for Docker/Compose):
|
||||
# ALLOWED_HOSTNAMES=yourdomain.com,www.yourdomain.com
|
||||
#
|
||||
# For local development:
|
||||
# app.allowedHostnames = 'localhost'
|
||||
#
|
||||
|
||||
1
.github/workflows/build-release.yml
vendored
1
.github/workflows/build-release.yml
vendored
@@ -123,7 +123,6 @@ jobs:
|
||||
.
|
||||
!.git
|
||||
!node_modules
|
||||
include-hidden-files: true
|
||||
retention-days: 1
|
||||
|
||||
docker:
|
||||
|
||||
219
.github/workflows/deploy-core.yml
vendored
219
.github/workflows/deploy-core.yml
vendored
@@ -1,219 +0,0 @@
|
||||
name: Deploy Core
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: 'Docker image tag to deploy'
|
||||
type: string
|
||||
required: true
|
||||
sha:
|
||||
description: 'Git commit SHA to deploy'
|
||||
type: string
|
||||
required: true
|
||||
description:
|
||||
description: 'Deployment description'
|
||||
type: string
|
||||
required: true
|
||||
pr_number:
|
||||
description: 'Pull request number (optional)'
|
||||
type: string
|
||||
required: false
|
||||
outputs:
|
||||
deployment_id:
|
||||
description: 'GitHub deployment ID'
|
||||
value: ${{ jobs.deploy.outputs.deployment_id }}
|
||||
status:
|
||||
description: 'Deployment status (success/failure)'
|
||||
value: ${{ jobs.deploy.outputs.status }}
|
||||
|
||||
concurrency:
|
||||
group: deploy-staging
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy to staging
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
environment:
|
||||
name: staging
|
||||
url: ${{ vars.DEPLOY_URL || 'https://dev.opensourcepos.org' }}
|
||||
deployment: false
|
||||
|
||||
outputs:
|
||||
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
|
||||
status: ${{ steps.webhook.outputs.status }}
|
||||
|
||||
steps:
|
||||
- name: Create GitHub Deployment
|
||||
id: deployment
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
IMAGE_TAG: ${{ inputs.image_tag }}
|
||||
REF_SHA: ${{ inputs.sha }}
|
||||
DESCRIPTION: ${{ inputs.description }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
DEPLOYMENT_ID=$(gh api "repos/${GITHUB_REPOSITORY}/deployments" \
|
||||
-X POST \
|
||||
-f ref="${REF_SHA}" \
|
||||
-f environment="staging" \
|
||||
-f description="${DESCRIPTION}" \
|
||||
-F auto_merge=false \
|
||||
-F required_contexts[] \
|
||||
--jq '.id')
|
||||
|
||||
if [ -z "$DEPLOYMENT_ID" ]; then
|
||||
echo "::error::Failed to create deployment"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "deployment_id=$DEPLOYMENT_ID" >> "$GITHUB_OUTPUT"
|
||||
echo "Created deployment: $DEPLOYMENT_ID"
|
||||
|
||||
- name: Set deployment status to in_progress
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
gh api "repos/${GITHUB_REPOSITORY}/deployments/${{ steps.deployment.outputs.deployment_id }}/statuses" \
|
||||
-X POST \
|
||||
-f state="in_progress" \
|
||||
-f description="Deployment in progress..." \
|
||||
-f log_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
|
||||
|
||||
- name: Trigger deployment webhook
|
||||
id: webhook
|
||||
env:
|
||||
DEPLOY_WEBHOOK_URL: ${{ secrets.DEPLOY_WEBHOOK_URL }}
|
||||
DEPLOY_WEBHOOK_SECRET: ${{ secrets.DEPLOY_WEBHOOK_SECRET }}
|
||||
DOCKER_REPO_NAME: ${{ secrets.DOCKER_REPO_NAME }}
|
||||
IMAGE_TAG: ${{ inputs.image_tag }}
|
||||
REF_SHA: ${{ inputs.sha }}
|
||||
DEPLOYMENT_ID: ${{ steps.deployment.outputs.deployment_id }}
|
||||
PR_NUMBER: ${{ inputs.pr_number }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "$DEPLOY_WEBHOOK_URL" ]; then
|
||||
echo "::error::DEPLOY_WEBHOOK_URL secret is not configured"
|
||||
echo "Please add the DEPLOY_WEBHOOK_URL secret in your repository settings"
|
||||
echo "status=failure" >> "$GITHUB_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_NAME="${DOCKER_REPO_NAME:-opensourcepos/opensourcepos}"
|
||||
REPO_NAMESPACE="${REPO_NAME%%/*}"
|
||||
REPO_SHORT_NAME="${REPO_NAME#*/}"
|
||||
PUSHED_AT=$(date +%s)
|
||||
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg callback_url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
|
||||
--argjson pushed_at "$PUSHED_AT" \
|
||||
--arg pusher "$GITHUB_ACTOR" \
|
||||
--arg tag "$IMAGE_TAG" \
|
||||
--arg repo_name "$REPO_NAME" \
|
||||
--arg name "$REPO_SHORT_NAME" \
|
||||
--arg namespace "$REPO_NAMESPACE" \
|
||||
--arg repo_url "https://hub.docker.com/r/${REPO_NAME}/" \
|
||||
--arg deployment_id "$DEPLOYMENT_ID" \
|
||||
--arg repository "$GITHUB_REPOSITORY" \
|
||||
--arg sha "$REF_SHA" \
|
||||
--arg run_id "$GITHUB_RUN_ID" \
|
||||
--arg actor "$GITHUB_ACTOR" \
|
||||
--argjson pr_number "$PR_NUMBER" \
|
||||
'{
|
||||
callback_url: $callback_url,
|
||||
push_data: {pushed_at: $pushed_at, pusher: $pusher, tag: $tag},
|
||||
repository: {repo_name: $repo_name, name: $name, namespace: $namespace, repo_url: $repo_url, status: "Active"},
|
||||
github_deployment: {id: $deployment_id, environment: "staging", repository: $repository, sha: $sha, run_id: $run_id, actor: $actor, pull_request: $pr_number}
|
||||
}')
|
||||
else
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg callback_url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
|
||||
--argjson pushed_at "$PUSHED_AT" \
|
||||
--arg pusher "$GITHUB_ACTOR" \
|
||||
--arg tag "$IMAGE_TAG" \
|
||||
--arg repo_name "$REPO_NAME" \
|
||||
--arg name "$REPO_SHORT_NAME" \
|
||||
--arg namespace "$REPO_NAMESPACE" \
|
||||
--arg repo_url "https://hub.docker.com/r/${REPO_NAME}/" \
|
||||
--arg deployment_id "$DEPLOYMENT_ID" \
|
||||
--arg repository "$GITHUB_REPOSITORY" \
|
||||
--arg sha "$REF_SHA" \
|
||||
--arg run_id "$GITHUB_RUN_ID" \
|
||||
--arg actor "$GITHUB_ACTOR" \
|
||||
'{
|
||||
callback_url: $callback_url,
|
||||
push_data: {pushed_at: $pushed_at, pusher: $pusher, tag: $tag},
|
||||
repository: {repo_name: $repo_name, name: $name, namespace: $namespace, repo_url: $repo_url, status: "Active"},
|
||||
github_deployment: {id: $deployment_id, environment: "staging", repository: $repository, sha: $sha, run_id: $run_id, actor: $actor}
|
||||
}')
|
||||
fi
|
||||
|
||||
echo "Sending webhook..."
|
||||
echo "Image: ${IMAGE_TAG}"
|
||||
echo "Environment: staging"
|
||||
|
||||
HEADERS=(-H "Content-Type: application/json")
|
||||
|
||||
if [ -n "$DEPLOY_WEBHOOK_SECRET" ]; then
|
||||
SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$DEPLOY_WEBHOOK_SECRET" | sed 's/.*= //')
|
||||
HEADERS+=(-H "X-Hub-Signature-256: sha256=$SIGNATURE")
|
||||
echo "Using HMAC-SHA256 signature verification"
|
||||
else
|
||||
echo "::warning::DEPLOY_WEBHOOK_SECRET not set - webhook calls will not be signed"
|
||||
echo "For security, configure DEPLOY_WEBHOOK_SECRET in your repository settings"
|
||||
fi
|
||||
|
||||
HTTP_CODE=$(curl -sS --connect-timeout 10 --max-time 120 \
|
||||
-o response.txt -w "%{http_code}" \
|
||||
-X POST \
|
||||
"${HEADERS[@]}" \
|
||||
-d "$PAYLOAD" \
|
||||
"$DEPLOY_WEBHOOK_URL") || HTTP_CODE="000"
|
||||
|
||||
echo "Response code: $HTTP_CODE"
|
||||
if [ -s response.txt ]; then
|
||||
cat response.txt
|
||||
fi
|
||||
|
||||
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
|
||||
echo "status=success" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "status=failure" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Set deployment status
|
||||
if: always()
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
IMAGE_TAG: ${{ inputs.image_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
STATE="${{ steps.webhook.outputs.status }}"
|
||||
|
||||
if [ "$STATE" = "success" ]; then
|
||||
DESCRIPTION=$(jq -nr --arg tag "$IMAGE_TAG" \
|
||||
'"Deployed image \($tag) to staging"')
|
||||
|
||||
gh api "repos/${GITHUB_REPOSITORY}/deployments/${{ steps.deployment.outputs.deployment_id }}/statuses" \
|
||||
-X POST \
|
||||
-f state="success" \
|
||||
-f description="$DESCRIPTION"
|
||||
else
|
||||
gh api "repos/${GITHUB_REPOSITORY}/deployments/${{ steps.deployment.outputs.deployment_id }}/statuses" \
|
||||
-X POST \
|
||||
-f state="failure" \
|
||||
-f description="Deployment failed"
|
||||
exit 1
|
||||
fi
|
||||
79
.github/workflows/deploy-pr.yml
vendored
79
.github/workflows/deploy-pr.yml
vendored
@@ -1,79 +0,0 @@
|
||||
name: PR Deploy
|
||||
|
||||
on:
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
concurrency:
|
||||
group: staging-deploy
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Prepare deployment
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event.review.state == 'approved' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
outputs:
|
||||
image_tag: ${{ steps.image.outputs.tag }}
|
||||
sha: ${{ github.event.pull_request.head.sha }}
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Get image tag
|
||||
id: image
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
IMAGE_TAG="pr-${PR_NUMBER}-${PR_SHA:0:7}"
|
||||
echo "tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
|
||||
|
||||
deploy:
|
||||
name: Deploy to staging
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/deploy-core.yml
|
||||
with:
|
||||
image_tag: ${{ needs.prepare.outputs.image_tag }}
|
||||
sha: ${{ needs.prepare.outputs.sha }}
|
||||
description: Deploy PR #${{ needs.prepare.outputs.pr_number }} to staging
|
||||
pr_number: ${{ needs.prepare.outputs.pr_number }}
|
||||
secrets: inherit
|
||||
|
||||
comment:
|
||||
name: Comment deployment status
|
||||
needs: [prepare, deploy]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
|
||||
PR_NUMBER: ${{ needs.prepare.outputs.pr_number }}
|
||||
REF_SHA: ${{ needs.prepare.outputs.sha }}
|
||||
STATUS: ${{ needs.deploy.outputs.status }}
|
||||
|
||||
steps:
|
||||
- name: Comment on PR
|
||||
run: |
|
||||
if [ "$STATUS" = "success" ]; then
|
||||
BODY=$(jq -nr --arg tag "$IMAGE_TAG" --arg sha "$REF_SHA" --arg url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
|
||||
'"✅ **Staging deployment completed**\n\n🔗 **URL**: https://dev.opensourcepos.org\n📦 **Image Tag**: `\($tag)`\n🔨 **Commit**: \($sha)\n\nView logs: \($url)"')
|
||||
else
|
||||
BODY=$(jq -nr --arg url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
|
||||
'"❌ **Staging deployment failed**\n\nCheck the [workflow logs](\($url)) for details."')
|
||||
fi
|
||||
|
||||
gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
|
||||
-X POST \
|
||||
-f body="$BODY"
|
||||
23
.github/workflows/deploy.yml
vendored
23
.github/workflows/deploy.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: 'Docker image tag to deploy (e.g., v3.4.0, latest)'
|
||||
required: true
|
||||
default: 'latest'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy to staging
|
||||
uses: ./.github/workflows/deploy-core.yml
|
||||
with:
|
||||
image_tag: ${{ inputs.image_tag }}
|
||||
sha: ${{ github.sha }}
|
||||
description: Deploy image ${{ inputs.image_tag }}
|
||||
secrets: inherit
|
||||
33
CHANGELOG.md
33
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.0...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,36 +34,10 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [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))
|
||||
- Add CI4 coding standards linter ([#3708](https://github.com/opensourcepos/opensourcepos/issues/3708)) by @jekkos in ([#4198](https://github.com/opensourcepos/opensourcepos/pull/4198))
|
||||
- Bump canvg from 3.0.10 to 3.0.11 by @dependabot in ([#4189](https://github.com/opensourcepos/opensourcepos/pull/4189))
|
||||
- Bump jspdf and jspdf-autotable by @dependabot in ([#4190](https://github.com/opensourcepos/opensourcepos/pull/4190))
|
||||
- Feature bump ci to 4.6.0 by @objecttothis in ([#4197](https://github.com/opensourcepos/opensourcepos/pull/4197))
|
||||
- Add Kurdish language option to UI by @BudsieBuds in ([#4210](https://github.com/opensourcepos/opensourcepos/pull/4210))
|
||||
- Convert language ku to ckb by @BudsieBuds in ([#4211](https://github.com/opensourcepos/opensourcepos/pull/4211))
|
||||
- Fix PHP 8.4 errors by @BudsieBuds in ([#4215](https://github.com/opensourcepos/opensourcepos/pull/4215))
|
||||
- Add default bootstrap to themes by @BudsieBuds in ([#4219](https://github.com/opensourcepos/opensourcepos/pull/4219))
|
||||
- Update language names by @BudsieBuds in ([#4218](https://github.com/opensourcepos/opensourcepos/pull/4218))
|
||||
- Update install docs by @BudsieBuds in ([#4217](https://github.com/opensourcepos/opensourcepos/pull/4217))
|
||||
- Convert menu icons to SVG by @BudsieBuds in ([#4220](https://github.com/opensourcepos/opensourcepos/pull/4220))
|
||||
- Enhance license handling by @BudsieBuds in ([#4223](https://github.com/opensourcepos/opensourcepos/pull/4223))
|
||||
- Fix datetime rendering ([#4226](https://github.com/opensourcepos/opensourcepos/issues/4226)) by @jekkos in ([#4227](https://github.com/opensourcepos/opensourcepos/pull/4227))
|
||||
- Fix datetime rendering by @jekkos in ([#4228](https://github.com/opensourcepos/opensourcepos/pull/4228))
|
||||
- Fix null error when sending by email a receipt of a sale that has no invoice by @diego-ramos in ([#4229](https://github.com/opensourcepos/opensourcepos/pull/4229))
|
||||
- Update Receivings.php to save form. by @odiea in ([#4231](https://github.com/opensourcepos/opensourcepos/pull/4231))
|
||||
- Update Cashups.php for ajax cashup total to work. by @odiea in ([#4238](https://github.com/opensourcepos/opensourcepos/pull/4238))
|
||||
- Coding style updates for PSR-12 compliance & improved readability by @BudsieBuds in ([#4204](https://github.com/opensourcepos/opensourcepos/pull/4204))
|
||||
- Fix Codeigniter disallowed characters error with payment types that have accents by @diego-ramos in ([#4232](https://github.com/opensourcepos/opensourcepos/pull/4232))
|
||||
- Fixed broken escape string for success & warning messages by @Franchovy in ([#4253](https://github.com/opensourcepos/opensourcepos/pull/4253))
|
||||
- Bugfix constraint migration fix by @objecttothis in ([#4230](https://github.com/opensourcepos/opensourcepos/pull/4230))
|
||||
- Fix item number lookup in sales/receivings ([#4212](https://github.com/opensourcepos/opensourcepos/issues/4212)) by @jekkos in ([#4250](https://github.com/opensourcepos/opensourcepos/pull/4250))
|
||||
|
||||
## [3.4.0] - 2025-03-23
|
||||
## [3.4.0] - 2025-02-06
|
||||
|
||||
- Translation updates (Spanish, Indonesian, Swedish, Urdu, Chinese, Thai, French, Dutch)
|
||||
- PHP `8.x` support
|
||||
- PHP 8.x support
|
||||
- Security fixes (XSS, SQLi)
|
||||
- Migration to Gulp as buildsystem
|
||||
- Decimal validation fix
|
||||
|
||||
@@ -1,85 +1,98 @@
|
||||
[comment]: # (Contributor Covenant 2.1 - from https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md)
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our community include:
|
||||
Contributor Covenant Code of Conduct
|
||||
Our Pledge
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
Our Standards
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall community
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of any kind
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
* Publishing others’ private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
Enforcement Responsibilities
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
Scope
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official email address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
Enforcement
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[INSERT CONTACT METHOD].
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
Enforcement Guidelines
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
1. Correction
|
||||
Community Impact: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
Consequence: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
2. Warning
|
||||
Community Impact: A violation through a single incident or series of
|
||||
actions.
|
||||
Consequence: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
3. Temporary Ban
|
||||
Community Impact: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
Consequence: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
4. Permanent Ban
|
||||
Community Impact: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
Consequence: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
Attribution
|
||||
This Code of Conduct is adapted from the Contributor Covenant,
|
||||
version 2.1, available at
|
||||
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
||||
Community Impact Guidelines were inspired by
|
||||
Mozilla’s code of conduct enforcement ladder.
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
@@ -13,8 +13,7 @@ RUN echo "date.timezone = \"\${PHP_TIMEZONE}\"" > /usr/local/etc/php/conf.d/time
|
||||
|
||||
WORKDIR /app
|
||||
COPY --chown=www-data:www-data . /app
|
||||
RUN chmod 750 /app/writable/logs /app/writable/uploads /app/writable/cache /app/public/uploads /app/public/uploads/item_pics \
|
||||
&& chmod 640 /app/writable/uploads/importCustomers.csv \
|
||||
RUN chmod 770 /app/writable/uploads /app/writable/logs /app/writable/cache \
|
||||
&& ln -s /app/*[^public] /var/www \
|
||||
&& rm -rf /var/www/html \
|
||||
&& ln -nsf /app/public /var/www/html
|
||||
|
||||
@@ -106,7 +106,7 @@ NOTE: If you're running non-release code, please make sure you always run the la
|
||||
|
||||
## 🏃 Keep the Machine Running
|
||||
|
||||
If you like our project, please consider buying us a coffee through the button below so we can keep adding features. Please star the project if you like it!
|
||||
If you like our project, please consider buying us a coffee through the button below so we can keep adding features.
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MUN6AEG7NY6H8)\
|
||||
Or refer to the [FUNDING.yml](.github/FUNDING.yml) file.
|
||||
|
||||
131
SECURITY.md
131
SECURITY.md
@@ -5,9 +5,8 @@
|
||||
- [Supported Versions](#supported-versions)
|
||||
- [Security Advisories](#security-advisories)
|
||||
- [Reporting a Vulnerability](#reporting-a-vulnerability)
|
||||
- [Disclosure Process](#disclosure-process)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow update -->
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Security Policy
|
||||
|
||||
@@ -22,116 +21,26 @@ We release patches for security vulnerabilities.
|
||||
|
||||
## Security Advisories
|
||||
|
||||
For a complete list of published and draft security advisories with CVE details, see our [GitHub Security Advisories page](https://github.com/opensourcepos/opensourcepos/security/advisories).
|
||||
The following security vulnerabilities have been published:
|
||||
|
||||
### High Severity
|
||||
|
||||
| CVE | Vulnerability | CVSS | Published | Fixed In | Credit |
|
||||
|-----|--------------|------|-----------|----------|--------|
|
||||
| [CVE-2025-68434](https://github.com/opensourcepos/opensourcepos/security/advisories/GHSA-wjm4-hfwg-5w5r) | CSRF leading to Admin Creation | 8.8 | 2025-12-17 | 3.4.2 | @Nixon-H, @jekkos |
|
||||
| [CVE-2025-68147](https://github.com/opensourcepos/opensourcepos/security/advisories/GHSA-xgr7-7pvw-fpmh) | Stored XSS in Return Policy | 8.1 | 2025-12-17 | 3.4.2 | @Nixon-H, @jekkos |
|
||||
| [CVE-2025-66924](https://github.com/opensourcepos/opensourcepos/security/advisories/GHSA-gv8j-f6gq-g59m) | Stored XSS in Item Kits | 7.2 | 2026-03-04 | 3.4.2 | @hungnqdz, @omkaryepre |
|
||||
|
||||
### Medium Severity
|
||||
|
||||
| CVE | Vulnerability | CVSS | Published | Fixed In | Credit |
|
||||
|-----|--------------|------|-----------|----------|--------|
|
||||
| [CVE-2025-68658](https://github.com/opensourcepos/opensourcepos/security/advisories/GHSA-32r8-8r9r-9chw) | Stored XSS in Company Name | 4.3 | 2026-01-13 | 3.4.2 | @hungnqdz |
|
||||
|
||||
For a complete list including draft advisories, see our [GitHub Security Advisories page](https://github.com/opensourcepos/opensourcepos/security/advisories).
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**Option 1: GitHub Security Advisory (Preferred)**
|
||||
Please report (suspected) security vulnerabilities to **[jeroen@steganos.dev](mailto:jeroen@steganos.dev)**.
|
||||
|
||||
1. Create a draft security advisory directly on GitHub:
|
||||
- Go to https://github.com/opensourcepos/opensourcepos/security/advisories
|
||||
- Click "New draft security advisory"
|
||||
- Fill in the vulnerability details using our [template below](#vulnerability-template)
|
||||
- Submit as **draft** (not published)
|
||||
|
||||
2. Notify us for triage:
|
||||
- Send an email to **[jeroen@steganos.dev](mailto:jeroen@steganos.dev)** with:
|
||||
- Subject: `[GHSA] Brief description of vulnerability`
|
||||
- Link to the draft advisory
|
||||
- Brief summary
|
||||
|
||||
**Option 2: Email Report**
|
||||
|
||||
Send vulnerability details to **[jeroen@steganos.dev](mailto:jeroen@steganos.dev)**.
|
||||
|
||||
You will receive a response within 48 hours. Confirmed vulnerabilities will be patched within a few days depending on complexity.
|
||||
|
||||
## Disclosure Process
|
||||
|
||||
### Timeline
|
||||
|
||||
| Step | Timeline | Action |
|
||||
|------|----------|--------|
|
||||
| 1. Report received | Day 0 | We acknowledge within 48 hours |
|
||||
| 2. Triage & confirmation | Day 1-3 | We validate the vulnerability |
|
||||
| 3. Fix development | Day 3-7 | We develop and test the fix |
|
||||
| 4. Patch release | Day 7-10 | We release a security patch |
|
||||
| 5. CVE request | Day 7-14 | We request CVE from GitHub (if applicable) |
|
||||
| 6. Advisory published | Day 14 | We publish the advisory with credit |
|
||||
| 7. Public disclosure | Day 14+ | Full disclosure after patch release |
|
||||
|
||||
### CVE Process
|
||||
|
||||
**We request CVE identifiers through GitHub's security advisory system.** This is the preferred and easiest method:
|
||||
|
||||
1. After we confirm and fix the vulnerability, we'll request a CVE through GitHub
|
||||
2. GitHub coordinates with MITRE on our behalf
|
||||
3. The CVE is automatically linked to the advisory
|
||||
4. You'll be credited as the reporter in the published advisory
|
||||
|
||||
**Already have a CVE?** If you've already obtained a CVE from another source (e.g., VulDB, CVE.MITRE.ORG), please include it in your report or advisory. We'll update our advisory to reference the existing CVE.
|
||||
|
||||
### No Bug Bounty Program
|
||||
|
||||
**Important:** Open Source Point of Sale does not offer a bug bounty program.
|
||||
|
||||
- All security research and vulnerability triage is done on a **voluntary basis** in our free time
|
||||
- We do not offer monetary rewards for vulnerability reports
|
||||
- We do credit reporters in published advisories (unless anonymity is requested)
|
||||
- We greatly appreciate the security research community's efforts to help improve project security
|
||||
|
||||
### Security Best Practices for Researchers
|
||||
|
||||
- **Do not** access, modify, or delete data that doesn't belong to you
|
||||
- **Do not** perform denial of service attacks
|
||||
- **Do not** publicly disclose vulnerabilities before we've had time to fix them
|
||||
- **Do** provide sufficient information to reproduce the vulnerability
|
||||
- **Do** allow us reasonable time to fix before public disclosure
|
||||
- **Do** report through official channels (GitHub advisories or email)
|
||||
|
||||
### Vulnerability Template
|
||||
|
||||
When creating a draft advisory, please include:
|
||||
|
||||
```
|
||||
## Summary
|
||||
[Brief description of the vulnerability]
|
||||
|
||||
## Impact
|
||||
- **Confidentiality:** [High/Medium/Low - what data can be exposed]
|
||||
- **Integrity:** [High/Medium/Low - what can be modified]
|
||||
- **Availability:** [High/Medium/Low - service disruption potential]
|
||||
- **Privilege Required:** [None/Low/High - authentication level needed]
|
||||
- **CVSS v3.1:** [Score] ([Vector string])
|
||||
|
||||
## Details
|
||||
[Technical details about the vulnerability]
|
||||
|
||||
**Affected Code:**
|
||||
```php
|
||||
// Path to affected file and vulnerable code
|
||||
```
|
||||
|
||||
**Attack Vector:**
|
||||
[How an attacker can exploit this]
|
||||
|
||||
## Proof of Concept
|
||||
```bash
|
||||
# Steps to reproduce
|
||||
```
|
||||
|
||||
## Patch
|
||||
[Suggested fix or approach]
|
||||
|
||||
## Affected Versions
|
||||
- OpenSourcePOS X.Y.Z and earlier
|
||||
|
||||
## Credit
|
||||
[Your GitHub username or preferred name]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Thank you to all security researchers who have contributed to making Open Source Point of Sale more secure.** Your voluntary efforts help protect thousands of users worldwide and contribute to a safer, more trustworthy free and open-source software ecosystem. We deeply appreciate your responsible disclosure and the time you invest in improving our project.
|
||||
|
||||
If you've reported a vulnerability and would like to discuss CVE coordination or have questions about the process, please reach out to us at [jeroen@steganos.dev](mailto:jeroen@steganos.dev).
|
||||
You will receive a response from us within 48 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days.
|
||||
@@ -58,9 +58,9 @@ class App extends BaseConfig
|
||||
* Allowed Hostnames in the Site URL other than the hostname in the baseURL.
|
||||
* If you want to accept multiple Hostnames, set this.
|
||||
*
|
||||
* Or via environment variable (useful for Docker/Compose):
|
||||
* ALLOWED_HOSTNAMES=example.com,www.example.com
|
||||
*
|
||||
* E.g.,
|
||||
* When your site URL ($baseURL) is 'http://example.com/', and your site
|
||||
* also accepts 'http://media.example.com/' and 'http://accounts.example.com/':
|
||||
* ['media.example.com', 'accounts.example.com']
|
||||
*
|
||||
* @var list<string>
|
||||
@@ -286,11 +286,7 @@ class App extends BaseConfig
|
||||
|
||||
// Solution for CodeIgniter 4 limitation: arrays cannot be set from .env
|
||||
// See: https://github.com/codeigniter4/CodeIgniter4/issues/7311
|
||||
// Support both: app.allowedHostnames (from .env) and ALLOWED_HOSTNAMES (from environment/Docker)
|
||||
$envAllowedHostnames = getenv('ALLOWED_HOSTNAMES');
|
||||
if ($envAllowedHostnames === false || trim($envAllowedHostnames) === '') {
|
||||
$envAllowedHostnames = getenv('app.allowedHostnames');
|
||||
}
|
||||
$envAllowedHostnames = getenv('app.allowedHostnames');
|
||||
if ($envAllowedHostnames !== false && trim($envAllowedHostnames) !== '') {
|
||||
$this->allowedHostnames = array_values(array_filter(
|
||||
array_map('trim', explode(',', $envAllowedHostnames)),
|
||||
@@ -331,7 +327,7 @@ class App extends BaseConfig
|
||||
$errorMessage =
|
||||
'Security: allowedHostnames is not configured. ' .
|
||||
'Host header injection protection is disabled. ' .
|
||||
'Set app.allowedHostnames in your .env file or ALLOWED_HOSTNAMES environment variable. ' .
|
||||
'Set app.allowedHostnames in your .env file. ' .
|
||||
'Example: app.allowedHostnames = "example.com,www.example.com" ' .
|
||||
'Received Host: ' . $httpHost;
|
||||
|
||||
|
||||
@@ -486,9 +486,10 @@ class Mimes
|
||||
/**
|
||||
* Attempts to determine the best mime type for the given file extension.
|
||||
*
|
||||
* @return string|null The mime type found, or none if unable to determine.
|
||||
* @param string $extension
|
||||
* @return array|string|null The mime type found, or none if unable to determine.
|
||||
*/
|
||||
public static function guessTypeFromExtension(string $extension)
|
||||
public static function guessTypeFromExtension(string $extension): array|string|null
|
||||
{
|
||||
$extension = trim(strtolower($extension), '. ');
|
||||
|
||||
@@ -506,7 +507,7 @@ class Mimes
|
||||
*
|
||||
* @return string|null The extension determined, or null if unable to match.
|
||||
*/
|
||||
public static function guessExtensionFromType(string $type, ?string $proposedExtension = null)
|
||||
public static function guessExtensionFromType(string $type, ?string $proposedExtension = null): ?string
|
||||
{
|
||||
$type = trim(strtolower($type), '. ');
|
||||
|
||||
@@ -522,7 +523,7 @@ class Mimes
|
||||
}
|
||||
|
||||
// Reverse check the mime type list if no extension was proposed.
|
||||
// This search is order sensitive!
|
||||
// This search is order-sensitive!
|
||||
foreach (static::$mimes as $ext => $types) {
|
||||
if (in_array($type, (array) $types, true)) {
|
||||
return $ext;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Config;
|
||||
use App\Models\Appconfig;
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Config\Database;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* This class holds the configuration options stored from the database so that on launch those settings can be cached
|
||||
@@ -14,7 +14,7 @@ use Config\Database;
|
||||
*/
|
||||
class OSPOS extends BaseConfig
|
||||
{
|
||||
public array $settings = [];
|
||||
public array $settings;
|
||||
public string $commit_sha1 = 'dev'; // TODO: Travis scripts need to be updated to replace this with the commit hash on build
|
||||
private CacheInterface $cache;
|
||||
|
||||
@@ -34,37 +34,25 @@ class OSPOS extends BaseConfig
|
||||
|
||||
if ($cache) {
|
||||
$this->settings = decode_array($cache);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::connect();
|
||||
|
||||
if (!$db->tableExists('app_config')) {
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
$appconfig = model(Appconfig::class);
|
||||
foreach ($appconfig->get_all()->getResult() as $app_config) {
|
||||
$this->settings[$app_config->key] = $app_config->value;
|
||||
}
|
||||
$this->cache->save('settings', encode_array($this->settings));
|
||||
} catch (DatabaseException $e) {
|
||||
// Database table doesn't exist yet (migrations haven't run)
|
||||
// Return empty settings to allow migration page to display
|
||||
$this->settings = [
|
||||
'language' => 'english',
|
||||
'language_code' => 'en',
|
||||
'company' => 'Home'
|
||||
];
|
||||
}
|
||||
|
||||
$appconfig = model(Appconfig::class);
|
||||
foreach ($appconfig->get_all()->getResult() as $app_config) {
|
||||
$this->settings[$app_config->key] = $app_config->value;
|
||||
}
|
||||
$this->cache->save('settings', encode_array($this->settings));
|
||||
} catch (\Exception $e) {
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private function getDefaultSettings(): array
|
||||
{
|
||||
return [
|
||||
'language' => 'english',
|
||||
'language_code' => 'en',
|
||||
'company' => 'Home',
|
||||
'barcode_type' => 'Code39'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
@@ -73,4 +61,4 @@ class OSPOS extends BaseConfig
|
||||
$this->cache->delete('settings');
|
||||
$this->set_settings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
|
||||
@@ -12,40 +12,6 @@ $routes->get('login', 'Login::index');
|
||||
$routes->post('login', 'Login::index');
|
||||
$routes->post('migrate', 'Login::migrate');
|
||||
|
||||
$routes->get('sales', 'Sales::getIndex');
|
||||
$routes->get('sales/customerDisplay', 'Sales::getCustomerDisplay');
|
||||
$routes->get('sales/itemSearch', 'Sales::getItemSearch');
|
||||
$routes->post('sales/selectCustomer', 'Sales::postSelectCustomer');
|
||||
$routes->post('sales/changeMode', 'Sales::postChangeMode');
|
||||
$routes->post('sales/setComment', 'Sales::postSetComment');
|
||||
$routes->post('sales/setInvoiceNumber', 'Sales::postSetInvoiceNumber');
|
||||
$routes->post('sales/setPaymentType', 'Sales::postSetPaymentType');
|
||||
$routes->post('sales/setPrintAfterSale', 'Sales::postSetPrintAfterSale');
|
||||
$routes->post('sales/setPriceWorkOrders', 'Sales::postSetPriceWorkOrders');
|
||||
$routes->post('sales/setEmailReceipt', 'Sales::postSetEmailReceipt');
|
||||
$routes->post('sales/addPayment', 'Sales::postAddPayment');
|
||||
$routes->post('sales/add', 'Sales::postAdd');
|
||||
$routes->post('sales/editItem/(:segment)', 'Sales::postEditItem/$1');
|
||||
$routes->post('sales/deleteItem/(:segment)', 'Sales::getDeleteItem/$1');
|
||||
$routes->post('sales/deletePayment/(:segment)', 'Sales::getDeletePayment/$1');
|
||||
$routes->post('sales/removeCustomer', 'Sales::getRemoveCustomer');
|
||||
$routes->post('sales/complete', 'Sales::postComplete');
|
||||
$routes->post('sales/cancel', 'Sales::postCancel');
|
||||
$routes->post('sales/suspend', 'Sales::postSuspend');
|
||||
$routes->post('sales/unsuspend', 'Sales::postUnsuspend');
|
||||
$routes->post('sales/checkInvoiceNumber', 'Sales::postCheckInvoiceNumber');
|
||||
$routes->post('sales/changeItemNumber', 'Sales::postChangeItemNumber');
|
||||
$routes->post('sales/changeItemName', 'Sales::postChangeItemName');
|
||||
$routes->post('sales/changeItemDescription', 'Sales::postChangeItemDescription');
|
||||
$routes->get('sales/suspended', 'Sales::getSuspended');
|
||||
$routes->get('sales/discardSuspendedSale', 'Sales::getDiscardSuspendedSale');
|
||||
$routes->get('sales/sales_keyboard_help', 'Sales::getSalesKeyboardHelp');
|
||||
$routes->get('sales/receipt/(:num)', 'Sales::getReceipt/$1');
|
||||
$routes->get('sales/invoice/(:num)', 'Sales::getInvoice/$1');
|
||||
$routes->get('sales/edit/(:num)', 'Sales::getEdit/$1');
|
||||
$routes->post('sales/delete/(:num)', 'Sales::postDelete/$1');
|
||||
$routes->post('sales/save/(:num)', 'Sales::postSave/$1');
|
||||
|
||||
$routes->add('no_access/index/(:segment)', 'No_access::index/$1');
|
||||
$routes->add('no_access/index/(:segment)/(:segment)', 'No_access::index/$1/$2');
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Session\Handlers\BaseHandler;
|
||||
use CodeIgniter\Session\Handlers\DatabaseHandler;
|
||||
use CodeIgniter\Session\Handlers\FileHandler;
|
||||
@@ -138,11 +139,7 @@ class Session extends BaseConfig
|
||||
$this->driver = FileHandler::class;
|
||||
$this->savePath = WRITEPATH . 'session';
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Database not available yet (e.g. fresh install before migrations).
|
||||
// Fall back to file-based sessions so the login/migration page
|
||||
// can still be served. Catches mysqli_sql_exception which is
|
||||
// not a subclass of DatabaseException but is a RuntimeException.
|
||||
} catch (DatabaseException $e) {
|
||||
$this->driver = FileHandler::class;
|
||||
$this->savePath = WRITEPATH . 'session';
|
||||
}
|
||||
|
||||
@@ -28,9 +28,12 @@ abstract class BaseController extends Controller
|
||||
// protected $session;
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param LoggerInterface $logger
|
||||
* @return void
|
||||
*/
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger): void
|
||||
{
|
||||
// Load here all helpers you want to be available in your controllers that extend BaseController.
|
||||
// Caution: Do not put the this below the parent::initController() call below.
|
||||
|
||||
@@ -221,7 +221,6 @@ class Config extends Secure_Controller
|
||||
*/
|
||||
public function getIndex(): string
|
||||
{
|
||||
$data['config'] = $this->config;
|
||||
$data['stock_locations'] = $this->stock_location->get_all()->getResultArray();
|
||||
$data['dinner_tables'] = $this->dinner_table->get_all()->getResultArray();
|
||||
$data['customer_rewards'] = $this->customer_rewards->get_all()->getResultArray();
|
||||
@@ -232,8 +231,6 @@ class Config extends Secure_Controller
|
||||
$data['line_sequence_options'] = $this->sale_lib->get_line_sequence_options();
|
||||
$data['register_mode_options'] = $this->sale_lib->get_register_mode_options();
|
||||
$data['invoice_type_options'] = $this->sale_lib->get_invoice_type_options();
|
||||
$data['keyboardShortcutOptions'] = $this->sale_lib->getKeyShortcutsOptions();
|
||||
$data['keyboardShortcuts'] = $this->sale_lib->getKeyShortcuts();
|
||||
$data['rounding_options'] = rounding_mode::get_rounding_options();
|
||||
$data['tax_code_options'] = $this->tax_lib->get_tax_code_options();
|
||||
$data['tax_category_options'] = $this->tax_lib->get_tax_category_options();
|
||||
@@ -241,28 +238,6 @@ class Config extends Secure_Controller
|
||||
$data['show_office_group'] = $this->module->get_show_office_group();
|
||||
$data['currency_code'] = $this->config['currency_code'] ?? '';
|
||||
$data['dbVersion'] = mysqli_get_server_info($this->db->getConnection());
|
||||
$data['scale_export_formats'] = [
|
||||
'txt' => 'TXT',
|
||||
'csv' => 'CSV',
|
||||
];
|
||||
$data['scale_export_charsets'] = [
|
||||
'windows-1256' => 'Windows-1256',
|
||||
'utf-8' => 'UTF-8',
|
||||
'windows-1252' => 'Windows-1252',
|
||||
];
|
||||
$data['scale_export_delimiters'] = [
|
||||
';' => ';',
|
||||
',' => ',',
|
||||
"\t" => 'Tab',
|
||||
];
|
||||
$data['scale_export_fields_options'] = [
|
||||
'legacy_code' => lang('Items.item_number'),
|
||||
'item_number' => lang('Items.item_number'),
|
||||
'repeat_item_number' => lang('Items.item_number'),
|
||||
'name' => lang('Items.name'),
|
||||
'unit_price' => lang('Items.unit_price'),
|
||||
'legacy_tail' => lang('Items.item_number'),
|
||||
];
|
||||
|
||||
// Load all the license statements, they are already XSS cleaned in the private function
|
||||
$data['licenses'] = $this->_licenses();
|
||||
@@ -416,7 +391,6 @@ class Config extends Secure_Controller
|
||||
'suggestions_third_column' => $this->validateSuggestionsColumn($this->request->getPost('suggestions_third_column'), 'other'),
|
||||
'giftcard_number' => $this->request->getPost('giftcard_number'),
|
||||
'derive_sale_quantity' => $this->request->getPost('derive_sale_quantity') != null,
|
||||
'customer_display_enabled' => $this->request->getPost('customer_display_enabled') != null,
|
||||
'multi_pack_enabled' => $this->request->getPost('multi_pack_enabled') != null,
|
||||
'include_hsn' => $this->request->getPost('include_hsn') != null,
|
||||
'category_dropdown' => $this->request->getPost('category_dropdown') != null
|
||||
@@ -424,9 +398,6 @@ class Config extends Secure_Controller
|
||||
|
||||
$this->module->set_show_office_group($this->request->getPost('show_office_group') != null);
|
||||
|
||||
$this->db->transStart();
|
||||
|
||||
$attributeSuccess = true;
|
||||
if ($batchSaveData['category_dropdown']) {
|
||||
$definitionData['definition_name'] = 'ospos_category';
|
||||
$definitionData['definition_flags'] = 0;
|
||||
@@ -434,16 +405,12 @@ class Config extends Secure_Controller
|
||||
$definitionData['definition_id'] = CATEGORY_DEFINITION_ID;
|
||||
$definitionData['deleted'] = 0;
|
||||
|
||||
$attributeSuccess = $this->attribute->saveDefinition($definitionData, CATEGORY_DEFINITION_ID);
|
||||
$this->attribute->saveDefinition($definitionData, CATEGORY_DEFINITION_ID);
|
||||
} elseif ($batchSaveData['category_dropdown'] == NO_DEFINITION_ID) {
|
||||
$attributeSuccess = $this->attribute->deleteDefinition(CATEGORY_DEFINITION_ID);
|
||||
$this->attribute->deleteDefinition(CATEGORY_DEFINITION_ID);
|
||||
}
|
||||
|
||||
$success = $attributeSuccess && $this->appconfig->batch_save($batchSaveData);
|
||||
|
||||
$this->db->transComplete();
|
||||
|
||||
$success = $success && $this->db->transStatus();
|
||||
$success = $this->appconfig->batch_save($batchSaveData);
|
||||
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
@@ -456,35 +423,32 @@ class Config extends Secure_Controller
|
||||
*/
|
||||
public function postCheckNumberLocale(): ResponseInterface
|
||||
{
|
||||
$numberLocale = $this->request->getPost('number_locale');
|
||||
$saveNumberLocale = $this->request->getPost('save_number_locale');
|
||||
$postedCurrencySymbol = $this->request->getPost('currency_symbol');
|
||||
$postedCurrencyCode = $this->request->getPost('currency_code');
|
||||
$number_locale = $this->request->getPost('number_locale');
|
||||
$save_number_locale = $this->request->getPost('save_number_locale');
|
||||
|
||||
$fmt = new NumberFormatter($numberLocale, NumberFormatter::CURRENCY);
|
||||
|
||||
// Use posted values if provided, otherwise fall back to locale defaults
|
||||
$currencySymbol = $postedCurrencySymbol !== '' ? $postedCurrencySymbol : $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL);
|
||||
$currencyCode = $postedCurrencyCode !== '' ? $postedCurrencyCode : $fmt->getTextAttribute(NumberFormatter::CURRENCY_CODE);
|
||||
|
||||
// Update saved locale if it changed
|
||||
if ($numberLocale !== $saveNumberLocale) {
|
||||
$saveNumberLocale = $numberLocale;
|
||||
$fmt = new NumberFormatter($number_locale, NumberFormatter::CURRENCY);
|
||||
if ($number_locale != $save_number_locale) {
|
||||
$currency_symbol = $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL);
|
||||
$currency_code = $fmt->getTextAttribute(NumberFormatter::CURRENCY_CODE);
|
||||
$save_number_locale = $number_locale;
|
||||
} else {
|
||||
$currency_symbol = empty($this->request->getPost('currency_symbol')) ? $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL) : $this->request->getPost('currency_symbol');
|
||||
$currency_code = empty($this->request->getPost('currency_code')) ? $fmt->getTextAttribute(NumberFormatter::CURRENCY_CODE) : $this->request->getPost('currency_code');
|
||||
}
|
||||
|
||||
if ($this->request->getPost('thousands_separator') == 'false') {
|
||||
$fmt->setTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
|
||||
}
|
||||
|
||||
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $currencySymbol);
|
||||
$numberLocaleExample = $fmt->format(1234567890.12300);
|
||||
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $currency_symbol);
|
||||
$number_local_example = $fmt->format(1234567890.12300);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => $numberLocaleExample != false,
|
||||
'save_number_locale' => $saveNumberLocale,
|
||||
'number_locale_example' => $numberLocaleExample,
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'currency_code' => $currencyCode,
|
||||
'success' => $number_local_example != false,
|
||||
'save_number_locale' => $save_number_locale,
|
||||
'number_locale_example' => $number_local_example,
|
||||
'currency_symbol' => $currency_symbol,
|
||||
'currency_code' => $currency_code,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -497,36 +461,13 @@ class Config extends Secure_Controller
|
||||
*/
|
||||
public function postSaveLocale(): ResponseInterface
|
||||
{
|
||||
$language = trim((string) $this->request->getPost('language'));
|
||||
$languageCode = 'en';
|
||||
$languageName = 'english';
|
||||
|
||||
if ($language !== '' && str_contains($language, ':')) {
|
||||
$exploded = array_map('trim', explode(':', $language, 2));
|
||||
|
||||
if (count($exploded) === 2) {
|
||||
$languageCode = htmlspecialchars($exploded[0]);
|
||||
$languageName = htmlspecialchars($exploded[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$exploded = explode(":", $this->request->getPost('language'));
|
||||
$currency_symbol = $this->request->getPost('currency_symbol');
|
||||
$secondaryCurrencyCode = strtoupper(trim((string) $this->request->getPost('secondary_currency_code')));
|
||||
|
||||
if (!preg_match('/^[A-Z]{3}$/', $secondaryCurrencyCode)) {
|
||||
$secondaryCurrencyCode = '';
|
||||
}
|
||||
|
||||
$batch_save_data = [
|
||||
'currency_symbol' => htmlspecialchars($currency_symbol ?? ''),
|
||||
'currency_code' => $this->request->getPost('currency_code'),
|
||||
'secondary_currency_enabled' => $this->request->getPost('secondary_currency_enabled') != null,
|
||||
'secondary_currency_symbol' => htmlspecialchars($this->request->getPost('secondary_currency_symbol') ?? ''),
|
||||
'secondary_currency_code' => $secondaryCurrencyCode,
|
||||
'secondary_currency_rate' => $this->request->getPost('secondary_currency_rate', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION),
|
||||
'secondary_currency_decimals' => $this->request->getPost('secondary_currency_decimals', FILTER_SANITIZE_NUMBER_INT),
|
||||
'language_code' => $languageCode,
|
||||
'language' => $languageName,
|
||||
'language_code' => $exploded[0],
|
||||
'language' => $exploded[1],
|
||||
'timezone' => $this->request->getPost('timezone'),
|
||||
'dateformat' => $this->request->getPost('dateformat'),
|
||||
'timeformat' => $this->request->getPost('timeformat'),
|
||||
@@ -980,7 +921,6 @@ class Config extends Secure_Controller
|
||||
'receipt_show_tax_ind' => $this->request->getPost('receipt_show_tax_ind') != null,
|
||||
'receipt_show_total_discount' => $this->request->getPost('receipt_show_total_discount') != null,
|
||||
'receipt_show_description' => $this->request->getPost('receipt_show_description') != null,
|
||||
'receipt_show_secondary_currency' => $this->request->getPost('receipt_show_secondary_currency') != null,
|
||||
'receipt_show_serialnumber' => $this->request->getPost('receipt_show_serialnumber') != null,
|
||||
'print_silently' => $this->request->getPost('print_silently') != null,
|
||||
'print_header' => $this->request->getPost('print_header') != null,
|
||||
@@ -996,44 +936,6 @@ class Config extends Secure_Controller
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves keyboard shortcut bindings.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveShortcuts(): ResponseInterface
|
||||
{
|
||||
$allowedShortcuts = array_keys($this->sale_lib->getKeyShortcutsOptions());
|
||||
$currentShortcuts = $this->sale_lib->getKeyShortcuts();
|
||||
$batchSaveData = [];
|
||||
|
||||
foreach ($currentShortcuts as $name => $shortcut) {
|
||||
$postedValue = trim((string) $this->request->getPost('key_' . $name));
|
||||
|
||||
if (!in_array($postedValue, $allowedShortcuts, true)) {
|
||||
$postedValue = $shortcut['value'];
|
||||
}
|
||||
|
||||
$batchSaveData['key_' . $name] = $postedValue;
|
||||
}
|
||||
|
||||
$duplicateValues = array_filter(array_count_values($batchSaveData), static fn(int $count): bool => $count > 1);
|
||||
if (!empty($duplicateValues)) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Config.shortcuts_duplicate_bindings')
|
||||
]);
|
||||
}
|
||||
|
||||
$success = $this->appconfig->batch_save($batchSaveData);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => $success,
|
||||
'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves invoice configuration. Used in app/Views/configs/invoice_config.php.
|
||||
*
|
||||
@@ -1113,6 +1015,3 @@ class Config extends Secure_Controller
|
||||
return in_array($column, $allowed, true) ? $column : $fallback;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class Home extends Secure_Controller
|
||||
public function getChangePassword(int $employeeId = NEW_ENTRY): ResponseInterface|string
|
||||
{
|
||||
$loggedInEmployee = $this->employee->get_logged_in_employee_info();
|
||||
$currentPersonId = (int) $loggedInEmployee->person_id;
|
||||
$currentPersonId = $loggedInEmployee->person_id;
|
||||
|
||||
$employeeId = $employeeId === NEW_ENTRY ? $currentPersonId : $employeeId;
|
||||
|
||||
@@ -68,11 +68,10 @@ class Home extends Secure_Controller
|
||||
public function postSave(int $employeeId = NEW_ENTRY): ResponseInterface
|
||||
{
|
||||
$currentUser = $this->employee->get_logged_in_employee_info();
|
||||
$currentPersonId = (int) $currentUser->person_id;
|
||||
|
||||
$employeeId = $employeeId === NEW_ENTRY ? $currentPersonId : $employeeId;
|
||||
$employeeId = $employeeId === NEW_ENTRY ? $currentUser->person_id : $employeeId;
|
||||
|
||||
if (!$this->employee->isAdmin($currentPersonId) && $employeeId !== $currentPersonId) {
|
||||
if (!$this->employee->isAdmin($currentUser->person_id) && $employeeId !== $currentUser->person_id) {
|
||||
return $this->response->setStatusCode(403)->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Employees.unauthorized_modify')
|
||||
|
||||
@@ -154,23 +154,8 @@ class Items extends Secure_Controller
|
||||
{
|
||||
helper('file');
|
||||
|
||||
// Security: Sanitize filename to prevent path traversal
|
||||
// Use basename() to strip directory components and prevent '../' attacks
|
||||
$pic_filename = basename(rawurldecode($pic_filename));
|
||||
$file_extension = strtolower(pathinfo($pic_filename, PATHINFO_EXTENSION));
|
||||
|
||||
// Validate file extension against system-configured allowed image types
|
||||
// Handle both legacy pipe-separated and current comma-separated formats
|
||||
// Fallback to types that GD library can process for thumbnail generation
|
||||
$allowed_types = $this->config['image_allowed_types'] ?? 'jpg,jpeg,gif,png,webp,bmp,tif,tiff';
|
||||
$allowed_extensions = strpos($allowed_types, '|') !== false
|
||||
? explode('|', $allowed_types)
|
||||
: explode(',', $allowed_types);
|
||||
|
||||
if (!in_array($file_extension, $allowed_extensions, true)) {
|
||||
return $this->response->setStatusCode(400)->setBody('Invalid file type');
|
||||
}
|
||||
|
||||
$pic_filename = rawurldecode($pic_filename);
|
||||
$file_extension = pathinfo($pic_filename, PATHINFO_EXTENSION);
|
||||
$images = glob("./uploads/item_pics/$pic_filename");
|
||||
$base_path = './uploads/item_pics/' . pathinfo($pic_filename, PATHINFO_FILENAME);
|
||||
|
||||
@@ -1055,20 +1040,14 @@ class Items extends Secure_Controller
|
||||
});
|
||||
|
||||
if (!$isFailedRow && $this->item->save_value($itemData, $itemId)) {
|
||||
if (!$this->save_tax_data($row, $itemData)) {
|
||||
$isFailedRow = true;
|
||||
}
|
||||
if (!$this->save_inventory_quantities($row, $itemData, $allowedStockLocations, $employeeId)) {
|
||||
$isFailedRow = true;
|
||||
}
|
||||
$this->save_tax_data($row, $itemData);
|
||||
$this->save_inventory_quantities($row, $itemData, $allowedStockLocations, $employeeId);
|
||||
$csvAttributeValues = $this->extractAttributeData($row);
|
||||
if (!$this->attribute->saveCSVRowAttributeData($csvAttributeValues, $itemData, $attributeData)) {
|
||||
$isFailedRow = true;
|
||||
}
|
||||
$isFailedRow = !$this->attribute->saveCSVRowAttributeData($csvAttributeValues, $itemData, $attributeData);
|
||||
if ($isFailedRow) {
|
||||
$failedRow = $key + 2;
|
||||
$failCodes[] = $failedRow;
|
||||
log_message('error', "CSV Item import failed on line $failedRow while saving item.");
|
||||
log_message('error', "CSV Item import failed on line $failedRow while saving attributes.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1258,15 +1237,13 @@ class Items extends Secure_Controller
|
||||
* @param array $item_data
|
||||
* @param array $allowed_locations
|
||||
* @param int $employee_id
|
||||
* @return bool Returns true on success, false on failure
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function save_inventory_quantities(array $row, array $item_data, array $allowed_locations, int $employee_id): bool
|
||||
private function save_inventory_quantities(array $row, array $item_data, array $allowed_locations, int $employee_id): void
|
||||
{
|
||||
// Quantities & Inventory Section
|
||||
$comment = lang('Items.inventory_CSV_import_quantity');
|
||||
$is_update = (bool)$row['Id'];
|
||||
$success = true;
|
||||
|
||||
foreach ($allowed_locations as $location_id => $location_name) {
|
||||
$item_quantity_data = ['item_id' => $item_data['item_id'], 'location_id' => $location_id];
|
||||
@@ -1280,22 +1257,20 @@ class Items extends Secure_Controller
|
||||
|
||||
if (!empty($row["location_$location_name"]) || $row["location_$location_name"] === '0') {
|
||||
$item_quantity_data['quantity'] = $row["location_$location_name"];
|
||||
$success &= $this->item_quantity->save_value($item_quantity_data, $item_data['item_id'], $location_id);
|
||||
$this->item_quantity->save_value($item_quantity_data, $item_data['item_id'], $location_id);
|
||||
|
||||
$csv_data['trans_inventory'] = $row["location_$location_name"];
|
||||
$success &= (bool)$this->inventory->insert($csv_data, false);
|
||||
$this->inventory->insert($csv_data, false);
|
||||
} elseif ($is_update) {
|
||||
continue;
|
||||
return;
|
||||
} else {
|
||||
$item_quantity_data['quantity'] = 0;
|
||||
$success &= $this->item_quantity->save_value($item_quantity_data, $item_data['item_id'], $location_id);
|
||||
$this->item_quantity->save_value($item_quantity_data, $item_data['item_id'], $location_id);
|
||||
|
||||
$csv_data['trans_inventory'] = 0;
|
||||
$success &= (bool)$this->inventory->insert($csv_data, false);
|
||||
$this->inventory->insert($csv_data, false);
|
||||
}
|
||||
}
|
||||
|
||||
return (bool)$success;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1303,9 +1278,8 @@ class Items extends Secure_Controller
|
||||
*
|
||||
* @param array $row
|
||||
* @param array $item_data
|
||||
* @return bool Returns true on success, false on failure
|
||||
*/
|
||||
private function save_tax_data(array $row, array $item_data): bool
|
||||
private function save_tax_data(array $row, array $item_data): void
|
||||
{
|
||||
$items_taxes_data = [];
|
||||
|
||||
@@ -1317,11 +1291,9 @@ class Items extends Secure_Controller
|
||||
$items_taxes_data[] = ['name' => $row['Tax 2 Name'], 'percent' => $row['Tax 2 Percent']];
|
||||
}
|
||||
|
||||
if (!empty($items_taxes_data)) {
|
||||
return $this->item_taxes->save_value($items_taxes_data, $item_data['item_id']);
|
||||
if (isset($items_taxes_data)) {
|
||||
$this->item_taxes->save_value($items_taxes_data, $item_data['item_id']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,13 +49,6 @@ class Login extends BaseController
|
||||
return view('login', $data);
|
||||
}
|
||||
|
||||
if (!$data['is_latest'] || $data['is_new_install']) {
|
||||
set_time_limit(3600);
|
||||
|
||||
$migration->setNamespace('App')->latest();
|
||||
return redirect()->to('login');
|
||||
}
|
||||
|
||||
$rules = ['username' => 'required|login_check[data]'];
|
||||
$messages = [
|
||||
'username' => [
|
||||
@@ -69,6 +62,13 @@ class Login extends BaseController
|
||||
|
||||
return view('login', $data);
|
||||
}
|
||||
|
||||
if (!$data['is_latest']) {
|
||||
set_time_limit(3600);
|
||||
|
||||
$migration->setNamespace('App')->latest();
|
||||
return redirect()->to('login');
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->to('home');
|
||||
@@ -79,18 +79,18 @@ class Login extends BaseController
|
||||
try {
|
||||
$migration = new MY_Migration(config('Migrations'));
|
||||
$migration->migrate_to_ci4();
|
||||
|
||||
|
||||
set_time_limit(3600);
|
||||
$migration->setNamespace('App')->latest();
|
||||
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'message' => 'Migration completed successfully'
|
||||
]);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Migration failed: ' . $e->getMessage());
|
||||
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => 'Migration failed: ' . $e->getMessage()
|
||||
|
||||
@@ -1246,15 +1246,13 @@ class Reports extends Secure_Controller
|
||||
public function get_payment_type(): array
|
||||
{
|
||||
return [
|
||||
'all' => lang('Common.none_selected_text'),
|
||||
'cash' => lang('Sales.cash'),
|
||||
'due' => lang('Sales.due'),
|
||||
'check' => lang('Sales.check'),
|
||||
'credit' => lang('Sales.credit'),
|
||||
'debit' => lang('Sales.debit'),
|
||||
'bank_transfer' => lang('Sales.bank_transfer'),
|
||||
'wallet' => lang('Sales.wallet'),
|
||||
'invoices' => lang('Sales.invoice')
|
||||
'all' => lang('Common.none_selected_text'),
|
||||
'cash' => lang('Sales.cash'),
|
||||
'due' => lang('Sales.due'),
|
||||
'check' => lang('Sales.check'),
|
||||
'credit' => lang('Sales.credit'),
|
||||
'debit' => lang('Sales.debit'),
|
||||
'invoices' => lang('Sales.invoice')
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -66,168 +66,12 @@ class Sales extends Secure_Controller
|
||||
$this->employee = model(Employee::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the shared secondary currency context to a view data array.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
private function _append_secondary_currency(array &$data): void
|
||||
{
|
||||
$secondaryCurrency = secondary_currency_context($this->config);
|
||||
$data['secondaryCurrency'] = $secondaryCurrency;
|
||||
|
||||
if (!$secondaryCurrency['show']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$displayFields = [
|
||||
'total' => 'secondaryTotalDisplay',
|
||||
'amount_due' => 'secondaryAmountDueDisplay',
|
||||
'cash_amount_due' => 'secondaryCashAmountDueDisplay',
|
||||
'non_cash_total' => 'secondaryNonCashTotalDisplay',
|
||||
'non_cash_amount_due' => 'secondaryNonCashAmountDueDisplay'
|
||||
];
|
||||
|
||||
foreach ($displayFields as $sourceField => $targetField) {
|
||||
if (array_key_exists($sourceField, $data)) {
|
||||
$data[$targetField] = to_secondary_currency((float) $data[$sourceField], $secondaryCurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getIndex(): ResponseInterface|string
|
||||
{
|
||||
$this->session->set('allow_temp_items', 1);
|
||||
return $this->_reload(); // TODO: Hungarian Notation
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the customer display popup.
|
||||
*
|
||||
* @return ResponseInterface|string
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function getCustomerDisplay(): ResponseInterface|string
|
||||
{
|
||||
if (($this->config['customer_display_enabled'] ?? false) != 1) {
|
||||
return $this->response->setStatusCode(404)->setBody('');
|
||||
}
|
||||
|
||||
if ($this->session->get('sale_id') == '') {
|
||||
$this->session->set('sale_id', NEW_ENTRY);
|
||||
}
|
||||
|
||||
$secondaryCurrency = secondary_currency_context($this->config);
|
||||
$secondaryCurrencyEnabled = (($this->config['secondary_currency_enabled'] ?? false) == 1);
|
||||
$cashRounding = $this->sale_lib->reset_cash_rounding();
|
||||
$showCustomerDisplay = $secondaryCurrencyEnabled && !empty($secondaryCurrency['rate']) && (float) $secondaryCurrency['rate'] > 0;
|
||||
$companyLines = preg_split("/\r\n|\r|\n/", (string) ($this->config['company'] ?? '')) ?: [];
|
||||
$companyName = array_shift($companyLines) ?? '';
|
||||
$companyDetails = trim(implode("\n", $companyLines));
|
||||
$secondaryCurrencySymbol = trim((string) ($this->config['secondary_currency_symbol'] ?? ''));
|
||||
$secondaryCurrencyCode = trim((string) ($this->config['secondary_currency_code'] ?? ''));
|
||||
$originalCurrencySymbol = trim((string) ($this->config['currency_symbol'] ?? ''));
|
||||
$customerDisplayCurrencyLabel = $secondaryCurrencyCode !== '' ? $secondaryCurrencyCode : ($secondaryCurrencySymbol !== '' ? $secondaryCurrencySymbol : 'LBP');
|
||||
$originalCurrencyLabel = $originalCurrencySymbol !== '' ? $originalCurrencySymbol : '$';
|
||||
$cartHasCustomerDisplay = $showCustomerDisplay;
|
||||
$cartColspan = $cartHasCustomerDisplay ? 6 : 5;
|
||||
$cartItemWidth = $cartHasCustomerDisplay ? 32 : 44;
|
||||
$cartPriceWidth = $cartHasCustomerDisplay ? 18 : 0;
|
||||
$cartOriginalWidth = $cartHasCustomerDisplay ? 18 : 26;
|
||||
$cartQuantityWidth = $cartHasCustomerDisplay ? 12 : 10;
|
||||
$cartDiscountWidth = $cartHasCustomerDisplay ? 10 : 9;
|
||||
$cartTotalWidth = $cartHasCustomerDisplay ? 10 : 11;
|
||||
|
||||
$data = [
|
||||
'cash_rounding' => $cashRounding,
|
||||
'cart' => $this->sale_lib->get_cart()
|
||||
];
|
||||
$customer_info = $this->_load_customer_data($this->sale_lib->get_customer(), $data, true);
|
||||
$data += [
|
||||
'customer_name' => $data['customer'] ?? lang('Sales.walk_in_customer'),
|
||||
'customer_reward_points' => (int) ($data['customer_rewards']['points'] ?? 0),
|
||||
'customer_reward_package' => $data['customer_rewards']['package_name'] ?? '',
|
||||
'giftcard_remainder' => $this->sale_lib->get_giftcard_remainder(),
|
||||
'rewards_remainder' => $this->sale_lib->get_rewards_remainder(),
|
||||
'customerName' => $data['customer'] ?? lang('Sales.walk_in_customer'),
|
||||
'customerRewardPoints' => (int) ($data['customer_rewards']['points'] ?? 0),
|
||||
'giftcardRemainder' => $this->sale_lib->get_giftcard_remainder()
|
||||
];
|
||||
|
||||
$tax_details = $this->tax_lib->get_taxes($data['cart']);
|
||||
$data += [
|
||||
'tax_exclusive_subtotal' => $this->sale_lib->get_subtotal(true, true),
|
||||
'taxes' => $tax_details[0],
|
||||
'discount' => $this->sale_lib->get_discount(),
|
||||
'payments' => $this->sale_lib->get_payments()
|
||||
];
|
||||
|
||||
$totals = $this->sale_lib->get_totals($tax_details[0]);
|
||||
$data += [
|
||||
'item_count' => $totals['item_count'],
|
||||
'total_units' => $totals['total_units'],
|
||||
'subtotal' => $totals['subtotal'],
|
||||
'total' => $totals['total'],
|
||||
'payments_total' => $totals['payment_total'],
|
||||
'payments_cover_total' => $totals['payments_cover_total'],
|
||||
'prediscount_subtotal' => $totals['prediscount_subtotal'],
|
||||
'cash_total' => $totals['cash_total'],
|
||||
'non_cash_total' => $totals['total'],
|
||||
'cash_amount_due' => $totals['cash_amount_due'],
|
||||
'non_cash_amount_due' => $totals['amount_due'],
|
||||
'cash_mode' => $this->session->get('cash_mode'),
|
||||
'selected_payment_type' => $this->sale_lib->get_payment_type(),
|
||||
'comment' => $this->sale_lib->get_comment(),
|
||||
'email_receipt' => $this->sale_lib->is_email_receipt(),
|
||||
'config' => $this->config,
|
||||
'mode' => $this->sale_lib->get_mode(),
|
||||
'rate' => (float) ($secondaryCurrency['rate'] ?? $this->config['secondary_currency_rate'] ?? 0),
|
||||
'secondaryCurrency' => $secondaryCurrency,
|
||||
'secondaryCurrencyEnabled' => $secondaryCurrencyEnabled,
|
||||
'showCustomerDisplay' => $showCustomerDisplay,
|
||||
'companyName' => $companyName,
|
||||
'companyDetails' => $companyDetails,
|
||||
'secondaryCurrencySymbol' => $secondaryCurrencySymbol,
|
||||
'secondaryCurrencyCode' => $secondaryCurrencyCode,
|
||||
'originalCurrencySymbol' => $originalCurrencySymbol,
|
||||
'customerDisplayCurrencyLabel' => $customerDisplayCurrencyLabel,
|
||||
'originalCurrencyLabel' => $originalCurrencyLabel,
|
||||
'cartHasCustomerDisplay' => $cartHasCustomerDisplay,
|
||||
'cartColspan' => $cartColspan,
|
||||
'cartItemWidth' => $cartItemWidth,
|
||||
'cartPriceWidth' => $cartPriceWidth,
|
||||
'cartOriginalWidth' => $cartOriginalWidth,
|
||||
'cartQuantityWidth' => $cartQuantityWidth,
|
||||
'cartDiscountWidth' => $cartDiscountWidth,
|
||||
'cartTotalWidth' => $cartTotalWidth,
|
||||
'items_module_allowed' => $this->employee->has_grant('items', $this->employee->get_logged_in_employee_info()->person_id),
|
||||
'change_price' => $this->employee->has_grant('sales_change_price', $this->employee->get_logged_in_employee_info()->person_id)
|
||||
];
|
||||
|
||||
$invoice_number = $this->sale_lib->get_invoice_number();
|
||||
if ($invoice_number == null || $invoice_number == '') {
|
||||
$invoice_number = $this->token_lib->render($this->config['sales_invoice_format'], [], false);
|
||||
}
|
||||
|
||||
$data += [
|
||||
'invoice_number' => $invoice_number,
|
||||
'print_after_sale' => $this->sale_lib->is_print_after_sale(),
|
||||
'price_work_orders' => $this->sale_lib->is_price_work_orders(),
|
||||
'pos_mode' => $data['mode'] == 'sale' || $data['mode'] == 'return',
|
||||
'quote_number' => $this->sale_lib->get_quote_number(),
|
||||
'work_order_number' => $this->sale_lib->get_work_order_number(),
|
||||
'amount_due' => $data['cash_mode'] && ($data['selected_payment_type'] === lang('Sales.cash') || $data['payments_total'] > 0) ? $totals['cash_amount_due'] : $totals['amount_due']
|
||||
];
|
||||
$data['amount_change'] = $data['amount_due'] * -1;
|
||||
$data['payment_change_due'] = ((float) $data['amount_due'] < 0)
|
||||
? abs((float) $data['amount_due'])
|
||||
: max(((float) $data['payments_total']) - ((float) $data['amount_due']), 0);
|
||||
$data['paymentChangeDue'] = $data['payment_change_due'];
|
||||
|
||||
return view('sales/customer_display', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the sale edit modal. Used in app/Views/sales/register.php.
|
||||
*
|
||||
@@ -970,7 +814,6 @@ class Sales extends Secure_Controller
|
||||
|
||||
// Resort and filter cart lines for printing
|
||||
$data['cart'] = $this->sale_lib->sort_and_filter_cart($data['cart']);
|
||||
$this->_append_secondary_currency($data);
|
||||
|
||||
if ($data['sale_id_num'] == NEW_ENTRY) {
|
||||
$data['error_message'] = lang('Sales.transaction_failed');
|
||||
@@ -1010,7 +853,6 @@ class Sales extends Secure_Controller
|
||||
$data['cart'] = $this->sale_lib->sort_and_filter_cart($data['cart']);
|
||||
|
||||
$data['barcode'] = null;
|
||||
$this->_append_secondary_currency($data);
|
||||
|
||||
$this->sale_lib->clear_all();
|
||||
return view('sales/work_order', $data);
|
||||
@@ -1038,7 +880,6 @@ class Sales extends Secure_Controller
|
||||
|
||||
$data['cart'] = $this->sale_lib->sort_and_filter_cart($data['cart']);
|
||||
$data['barcode'] = null;
|
||||
$this->_append_secondary_currency($data);
|
||||
|
||||
$this->sale_lib->clear_all();
|
||||
return view('sales/quote', $data);
|
||||
@@ -1057,7 +898,6 @@ class Sales extends Secure_Controller
|
||||
$data['sale_id'] = 'POS ' . $data['sale_id_num'];
|
||||
|
||||
$data['cart'] = $this->sale_lib->sort_and_filter_cart($data['cart']);
|
||||
$this->_append_secondary_currency($data);
|
||||
|
||||
if ($data['sale_id_num'] == NEW_ENTRY) {
|
||||
$data['error_message'] = lang('Sales.transaction_failed');
|
||||
@@ -1097,10 +937,7 @@ class Sales extends Secure_Controller
|
||||
new Token_customer((array)$sale_data)
|
||||
];
|
||||
$text = $this->token_lib->render($text, $tokens);
|
||||
$sale_data['mimetype'] = $this->email_lib->getLogoMimeType();
|
||||
|
||||
// Build img_tag for email views that need it (receipt_email.php)
|
||||
$sale_data['img_tag'] = $this->email_lib->buildLogoImgTag();
|
||||
$sale_data['mimetype'] = mime_content_type(FCPATH . 'uploads/' . $this->config['company_logo']);
|
||||
|
||||
// Generate email attachment: invoice in PDF format
|
||||
$view = Services::renderer();
|
||||
@@ -1137,7 +974,13 @@ class Sales extends Secure_Controller
|
||||
|
||||
if (!empty($sale_data['customer_email'])) {
|
||||
$sale_data['barcode'] = $this->barcode_lib->generate_receipt_barcode($sale_data['sale_id']);
|
||||
$sale_data['img_tag'] = $this->email_lib->buildLogoImgTag();
|
||||
$sale_data['img_tag'] = '';
|
||||
|
||||
$logo_path = FCPATH . 'uploads/' . $this->config['company_logo'];
|
||||
if (!empty($this->config['company_logo']) && file_exists($logo_path)) {
|
||||
$logo_data = base64_encode(file_get_contents($logo_path));
|
||||
$sale_data['img_tag'] = '<img id="image" src="data:image/png;base64,' . $logo_data . '" alt="company_logo">';
|
||||
}
|
||||
|
||||
$to = $sale_data['customer_email'];
|
||||
$subject = lang('Sales.receipt');
|
||||
@@ -1318,7 +1161,6 @@ class Sales extends Secure_Controller
|
||||
$invoice_type = 'invoice';
|
||||
}
|
||||
$data['invoice_view'] = $invoice_type;
|
||||
$this->_append_secondary_currency($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -1385,7 +1227,6 @@ class Sales extends Secure_Controller
|
||||
}
|
||||
|
||||
$data['amount_change'] = $data['amount_due'] * -1;
|
||||
$this->_append_secondary_currency($data);
|
||||
|
||||
$data['comment'] = $this->sale_lib->get_comment();
|
||||
$data['email_receipt'] = $this->sale_lib->is_email_receipt();
|
||||
@@ -1925,5 +1766,3 @@ class Sales extends Secure_Controller
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
FROM alpine:3.14
|
||||
LABEL maintainer="jekkos"
|
||||
MAINTAINER jekkos
|
||||
|
||||
ADD database.sql /docker-entrypoint-initdb.d/database.sql
|
||||
VOLUME /docker-entrypoint-initdb.d
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class Migration_Upgrade_To_3_1_1 extends Migration
|
||||
@@ -18,37 +17,7 @@ class Migration_Upgrade_To_3_1_1 extends Migration
|
||||
public function up(): void
|
||||
{
|
||||
helper('migration');
|
||||
|
||||
// MariaDB blocks CONVERT TO CHARACTER SET on tables with FK constraints.
|
||||
// Drop all FKs across affected tables before running the SQL script, recreate after.
|
||||
$fkColumns = [
|
||||
['modules', 'module_id'],
|
||||
['stock_locations', 'location_id'],
|
||||
['permissions', 'permission_id'],
|
||||
['people', 'person_id'],
|
||||
['suppliers', 'supplier_id'],
|
||||
['items', 'item_id'],
|
||||
['item_kits', 'item_kit_id'],
|
||||
['sales', 'sale_id'],
|
||||
['receivings', 'receiving_id'],
|
||||
['employees', 'employee_id'],
|
||||
['customers', 'person_id'],
|
||||
];
|
||||
|
||||
$constraints = [];
|
||||
foreach ($fkColumns as [$table, $column]) {
|
||||
foreach (dropAllForeignKeyConstraints($table, $column) as $c) {
|
||||
$constraints[$c['constraintName']] = $c;
|
||||
}
|
||||
}
|
||||
|
||||
if (!execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql')) {
|
||||
throw new DatabaseException('Migration script 3.0.2_to_3.1.1.sql failed. Check logs for details.');
|
||||
}
|
||||
|
||||
$droppedTables = ['sales_suspended', 'sales_suspended_items', 'sales_suspended_items_taxes', 'sales_suspended_payments'];
|
||||
$toRecreate = array_filter($constraints, fn($c) => !in_array($c['tableName'], $droppedTables, true));
|
||||
recreateForeignKeyConstraints(array_values($toRecreate));
|
||||
execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddShortcutKeys extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$shortcutValues = [
|
||||
['key' => 'key_cancel', 'value' => '27 | ESC'],
|
||||
['key' => 'key_items', 'value' => '49 | ALT + 1'],
|
||||
['key' => 'key_customers', 'value' => '50 | ALT + 2'],
|
||||
['key' => 'key_suspend', 'value' => '51 | ALT + 3'],
|
||||
['key' => 'key_suspended', 'value' => '52 | ALT + 4'],
|
||||
['key' => 'key_amount', 'value' => '53 | ALT + 5'],
|
||||
['key' => 'key_payment', 'value' => '54 | ALT + 6'],
|
||||
['key' => 'key_complete', 'value' => '55 | ALT + 7'],
|
||||
['key' => 'key_finish', 'value' => '56 | ALT + 8'],
|
||||
['key' => 'key_help', 'value' => '57 | ALT + 9'],
|
||||
];
|
||||
|
||||
$this->db->table('app_config')->ignore(true)->insertBatch($shortcutValues);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$shortcutKeys = [
|
||||
'key_cancel',
|
||||
'key_items',
|
||||
'key_customers',
|
||||
'key_suspend',
|
||||
'key_suspended',
|
||||
'key_amount',
|
||||
'key_payment',
|
||||
'key_complete',
|
||||
'key_finish',
|
||||
'key_help',
|
||||
];
|
||||
|
||||
$this->db->table('app_config')
|
||||
->whereIn('key', $shortcutKeys)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
@@ -327,6 +327,19 @@ INSERT INTO `ospos_sales_items` (sale_id, item_id, description, serialnumber, li
|
||||
INSERT INTO `ospos_sales_payments` (sale_id, payment_type, payment_amount) SELECT sale_id, payment_type, payment_amount FROM `ospos_sales_suspended_payments`;
|
||||
INSERT INTO `ospos_sales_items_taxes` (sale_id, item_id, line, name, percent) SELECT sale_id, item_id, line, name, percent FROM `ospos_sales_suspended_items_taxes`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended_payments` DROP FOREIGN KEY `ospos_sales_suspended_payments_ibfk_1`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended_items_taxes` DROP FOREIGN KEY `ospos_sales_suspended_items_taxes_ibfk_1`;
|
||||
ALTER TABLE `ospos_sales_suspended_items_taxes` DROP FOREIGN KEY `ospos_sales_suspended_items_taxes_ibfk_2`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended_items` DROP FOREIGN KEY `ospos_sales_suspended_items_ibfk_1`;
|
||||
ALTER TABLE `ospos_sales_suspended_items` DROP FOREIGN KEY `ospos_sales_suspended_items_ibfk_2`;
|
||||
ALTER TABLE `ospos_sales_suspended_items` DROP FOREIGN KEY `ospos_sales_suspended_items_ibfk_3`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended` DROP FOREIGN KEY `ospos_sales_suspended_ibfk_1`;
|
||||
ALTER TABLE `ospos_sales_suspended` DROP FOREIGN KEY `ospos_sales_suspended_ibfk_2`;
|
||||
ALTER TABLE `ospos_sales_suspended` DROP FOREIGN KEY `ospos_sales_suspended_ibfk_3`;
|
||||
|
||||
DROP TABLE `ospos_sales_suspended_payments`, `ospos_sales_suspended_items_taxes`, `ospos_sales_suspended_items`, `ospos_sales_suspended`;
|
||||
|
||||
--
|
||||
|
||||
@@ -140,7 +140,7 @@ CREATE TABLE IF NOT EXISTS `ospos_expense_categories` (
|
||||
`category_name` varchar(255) DEFAULT NULL,
|
||||
`category_description` varchar(255) NOT NULL,
|
||||
`deleted` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- Table structure for table `ospos_expenses`
|
||||
@@ -154,7 +154,7 @@ CREATE TABLE IF NOT EXISTS `ospos_expenses` (
|
||||
`description` varchar(255) NOT NULL,
|
||||
`employee_id` int(10) NOT NULL,
|
||||
`deleted` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- Indexes for table `ospos_expense_categories`
|
||||
|
||||
@@ -75,7 +75,7 @@ CREATE TABLE `ospos_cash_up` (
|
||||
`open_employee_id` int(10) NOT NULL,
|
||||
`close_employee_id` int(10) NOT NULL,
|
||||
`deleted` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Indexes for table `ospos_cash_up`
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS `ospos_tax_codes` (
|
||||
`state` varchar(255) NOT NULL DEFAULT '',
|
||||
`deleted` int(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`tax_code_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
ALTER TABLE `ospos_customers`
|
||||
ADD COLUMN `tax_id` varchar(32) NOT NULL DEFAULT '' AFTER `taxable`,
|
||||
@@ -59,7 +59,7 @@ CREATE TABLE `ospos_sales_taxes` (
|
||||
`rounding_code` tinyint(2) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`sales_taxes_id`),
|
||||
KEY `print_sequence` (`sale_id`,`print_sequence`,`tax_group`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ospos_tax_jurisdictions` (
|
||||
`jurisdiction_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
@@ -71,7 +71,7 @@ CREATE TABLE IF NOT EXISTS `ospos_tax_jurisdictions` (
|
||||
`cascade_sequence` tinyint(2) NOT NULL DEFAULT 0,
|
||||
`deleted` int(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`jurisdiction_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
|
||||
|
||||
ALTER TABLE `ospos_suppliers`
|
||||
ADD COLUMN `tax_id` varchar(32) DEFAULT NULL AFTER `account_number`;
|
||||
@@ -89,7 +89,7 @@ CREATE TABLE IF NOT EXISTS `ospos_tax_rates` (
|
||||
`tax_rate` decimal(15,4) NOT NULL DEFAULT 0.0000,
|
||||
`tax_rounding_code` tinyint(2) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`tax_rate_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Add support for sales tax report
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ CREATE TABLE `ospos_sales_payments` (
|
||||
`reference_code` varchar(40) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`payment_id`),
|
||||
KEY `payment_sale` (`sale_id`, `payment_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO ospos_sales_payments (sale_id, payment_type, payment_amount, payment_user)
|
||||
SELECT payments.sale_id, payments.payment_type, payments.payment_amount, sales.employee_id
|
||||
|
||||
@@ -22,7 +22,7 @@ function current_language_code(bool $load_system_language = false): string
|
||||
}
|
||||
}
|
||||
|
||||
return $config['language_code'] ?? DEFAULT_LANGUAGE_CODE;
|
||||
return $config->language_code ?? DEFAULT_LANGUAGE_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ function current_language(bool $load_system_language = false): string
|
||||
}
|
||||
}
|
||||
|
||||
return $config['language'] ?? DEFAULT_LANGUAGE;
|
||||
return $config->language ?? DEFAULT_LANGUAGE_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,74 +365,6 @@ function to_currency_no_money(?string $number): string
|
||||
return to_decimals($number, 'currency_decimals');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the secondary currency rendering context from app config values.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array{show:bool,rate:float,symbol:string,code:string,decimals:int}
|
||||
*/
|
||||
function secondary_currency_context(array $config): array
|
||||
{
|
||||
$rate = (float) ($config['secondary_currency_rate'] ?? 0);
|
||||
$symbol = trim((string) ($config['secondary_currency_symbol'] ?? ''));
|
||||
$code = trim((string) ($config['secondary_currency_code'] ?? ''));
|
||||
$decimals = (int) ($config['secondary_currency_decimals'] ?? ($config['currency_decimals'] ?? DEFAULT_PRECISION));
|
||||
|
||||
return [
|
||||
'show' => (($config['secondary_currency_enabled'] ?? false) == 1) && $rate > 0,
|
||||
'rate' => $rate,
|
||||
'symbol' => $symbol,
|
||||
'code' => $code,
|
||||
'decimals' => $decimals,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a value in the secondary currency.
|
||||
*
|
||||
* @param float|int|string|null $number
|
||||
* @param array{show:bool,rate:float,symbol:string,code:string,decimals:int} $secondaryCurrency
|
||||
* @return string
|
||||
*/
|
||||
function to_secondary_currency(float|int|string|null $number, array $secondaryCurrency): string
|
||||
{
|
||||
if (!isset($number) || !$secondaryCurrency['show']) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$config = config(OSPOS::class)->settings;
|
||||
$amount = (float) $number * (float) $secondaryCurrency['rate'];
|
||||
$fmt = new NumberFormatter($config['number_locale'], NumberFormatter::CURRENCY);
|
||||
$fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $secondaryCurrency['decimals']);
|
||||
$fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $secondaryCurrency['decimals']);
|
||||
|
||||
if (empty($config['thousands_separator'])) {
|
||||
$fmt->setTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
|
||||
}
|
||||
|
||||
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $secondaryCurrency['symbol'] !== '' ? $secondaryCurrency['symbol'] : ($secondaryCurrency['code'] !== '' ? $secondaryCurrency['code'] : ''));
|
||||
|
||||
return $fmt->format($amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the secondary and primary currency amounts together.
|
||||
*
|
||||
* @param float|int|string|null $number
|
||||
* @param array{show:bool,rate:float,symbol:string,code:string,decimals:int} $secondaryCurrency
|
||||
* @return string
|
||||
*/
|
||||
function to_secondary_currency_dual(float|int|string|null $number, array $secondaryCurrency): string
|
||||
{
|
||||
$secondary = to_secondary_currency($number, $secondaryCurrency);
|
||||
|
||||
if ($secondary === '') {
|
||||
return to_currency((string) $number);
|
||||
}
|
||||
|
||||
return $secondary . '<br>' . to_currency((string) $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $number
|
||||
* @return string
|
||||
|
||||
@@ -172,7 +172,6 @@ function dropAllForeignKeyConstraints(string $table, string $column): array {
|
||||
WHERE kcu.TABLE_SCHEMA = DATABASE()
|
||||
AND ((kcu.REFERENCED_TABLE_NAME = '" . $db->getPrefix() . "$table' AND kcu.REFERENCED_COLUMN_NAME = '$column')
|
||||
OR (kcu.TABLE_NAME = '" . $db->getPrefix() . "$table' AND kcu.COLUMN_NAME = '$column'))
|
||||
AND rc.CONSTRAINT_NAME IS NOT NULL
|
||||
");
|
||||
|
||||
$deletedConstraints = [];
|
||||
|
||||
@@ -9,7 +9,6 @@ return [
|
||||
"amount_due" => "Amount Due",
|
||||
"amount_tendered" => "Amount Tendered",
|
||||
"authorized_signature" => "Authorised Signature",
|
||||
"bank_transfer" => "Bank Transfer",
|
||||
"cancel_sale" => "Cancel",
|
||||
"cash" => "Cash",
|
||||
"cash_1" => "",
|
||||
@@ -224,7 +223,6 @@ return [
|
||||
"update" => "Update",
|
||||
"upi" => "UPI",
|
||||
"visa" => "",
|
||||
"wallet" => "Wallet",
|
||||
"wholesale" => "",
|
||||
"work_order" => "Work Order",
|
||||
"work_order_number" => "Work Order Number",
|
||||
|
||||
@@ -268,19 +268,10 @@ return [
|
||||
"receipt_show_company_name" => "Show Company Name",
|
||||
"receipt_show_description" => "Show Description",
|
||||
"receipt_show_serialnumber" => "Show Serial Number",
|
||||
"receipt_show_secondary_currency" => "Show Secondary Currency",
|
||||
"receipt_show_tax_ind" => "Show Tax Indicator",
|
||||
"receipt_show_taxes" => "Show Taxes",
|
||||
"receipt_show_total_discount" => "Show Total Discount",
|
||||
"receipt_template" => "Receipt Template",
|
||||
"secondary_currency" => "Secondary Currency",
|
||||
"secondary_currency_decimals" => "Secondary Currency Decimals",
|
||||
"secondary_currency_code" => "Secondary Currency Code",
|
||||
"secondary_currency_enable" => "Enable Secondary Currency",
|
||||
"secondary_currency_enable_tooltip" => "Show secondary currency fields and print/display values across the app.",
|
||||
"secondary_currency_rate" => "Secondary Currency Rate",
|
||||
"secondary_currency_settings" => "Secondary Currency Settings",
|
||||
"secondary_currency_symbol" => "Secondary Currency Symbol",
|
||||
"receiving_calculate_average_price" => "Calc avg. Price (Receiving)",
|
||||
"recv_invoice_format" => "Receivings Invoice Format",
|
||||
"register_mode_default" => "Default Register Mode",
|
||||
@@ -297,7 +288,6 @@ return [
|
||||
"security_issue" => "Security Vulnerability Warning",
|
||||
"server_notice" => "Please use the below info for issue reporting.",
|
||||
"service_charge" => "",
|
||||
"customer_display" => "Customer Display",
|
||||
"show_due_enable" => "",
|
||||
"show_office_group" => "Show office icon",
|
||||
"statistics" => "Send Statistics",
|
||||
@@ -340,5 +330,3 @@ return [
|
||||
"work_order_enable" => "Work Order Support",
|
||||
"work_order_format" => "Work Order Format",
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ return [
|
||||
"account_number" => "Account #",
|
||||
"add_payment" => "Add Payment",
|
||||
"amount_due" => "Amount Due",
|
||||
"amount_due_lbp" => "Amount Due LBP",
|
||||
"amount_tendered" => "Amount Tendered",
|
||||
"authorized_signature" => "Authorized Signature",
|
||||
"cancel_sale" => "Cancel",
|
||||
@@ -20,8 +19,6 @@ return [
|
||||
"cash_deposit" => "Cash Deposit",
|
||||
"cash_filter" => "Cash",
|
||||
"change_due" => "Change Due",
|
||||
"change" => "Change",
|
||||
"currency_rate" => "Currency Rate",
|
||||
"change_price" => "Change Selling Price",
|
||||
"check" => "Check",
|
||||
"check_balance" => "Check remainder",
|
||||
@@ -43,7 +40,6 @@ return [
|
||||
"customer_address" => "Address",
|
||||
"customer_discount" => "Discount",
|
||||
"customer_email" => "Email",
|
||||
"customer_name" => "Customer Name",
|
||||
"customer_location" => "Location",
|
||||
"customer_mailchimp_status" => "MailChimp Status",
|
||||
"customer_optional" => "(Required for Due Payments)",
|
||||
@@ -77,6 +73,12 @@ return [
|
||||
"employee" => "Employee",
|
||||
"entry" => "Entry",
|
||||
"error_editing_item" => "Error editing item",
|
||||
"negative_price_invalid" => "Price cannot be negative.",
|
||||
"negative_quantity_invalid" => "Quantity cannot be negative.",
|
||||
"negative_discount_invalid" => "Discount cannot be negative.",
|
||||
"discount_percent_exceeds_100" => "Percentage discount cannot exceed 100%.",
|
||||
"discount_exceeds_item_total" => "Discount cannot exceed the item total.",
|
||||
"negative_total_invalid" => "Sale total cannot be negative. Check item discounts and quantities.",
|
||||
"find_or_scan_item" => "Find or Scan Item",
|
||||
"find_or_scan_item_or_receipt" => "Find or Scan Item or Receipt",
|
||||
"giftcard" => "Gift Card",
|
||||
@@ -107,7 +109,6 @@ return [
|
||||
"item_name" => "Item Name",
|
||||
"item_number" => "Item #",
|
||||
"item_out_of_stock" => "Item is out of stock.",
|
||||
"items" => "Items",
|
||||
"key_browser" => "Helpful Shortcuts",
|
||||
"key_cancel" => "Cancels Current Quote/Invoice/Sale",
|
||||
"key_customer_search" => "Customer Search",
|
||||
@@ -149,9 +150,7 @@ return [
|
||||
"payment_type" => "Type",
|
||||
"payments" => "",
|
||||
"payments_total" => "Payments Total",
|
||||
"loyalty_reward_points" => "Loyalty Reward Points",
|
||||
"price" => "Price",
|
||||
"price_with_currency" => "Price (%s)",
|
||||
"print_after_sale" => "Print after Sale",
|
||||
"quantity" => "Quantity",
|
||||
"quantity_less_than_reorder_level" => "Warning: Desired Quantity is below Reorder Level for that Item.",
|
||||
@@ -167,13 +166,10 @@ return [
|
||||
"receipt_number" => "Sale #",
|
||||
"receipt_sent" => "Receipt sent to",
|
||||
"receipt_unsent" => "Receipt failed to be sent to",
|
||||
"rate" => "Rate",
|
||||
"refund" => "Refund Type",
|
||||
"register" => "Sales Register",
|
||||
"remove_customer" => "Remove Customer",
|
||||
"remove_discount" => "",
|
||||
"customer_display" => "Customer Display",
|
||||
"summary" => "Summary",
|
||||
"return" => "Return",
|
||||
"rewards" => "Reward Points",
|
||||
"rewards_balance" => "Reward Points Balance",
|
||||
@@ -185,7 +181,6 @@ return [
|
||||
"sales_total" => "",
|
||||
"select_customer" => "Select Customer",
|
||||
"selected_customer" => "Selected Customer",
|
||||
"walk_in_customer" => "Walk-in Customer",
|
||||
"send_invoice" => "Send Invoice",
|
||||
"send_quote" => "Send Quote",
|
||||
"send_receipt" => "Send Receipt",
|
||||
@@ -216,7 +211,6 @@ return [
|
||||
"tax_percent" => "Tax %",
|
||||
"taxed_ind" => "T",
|
||||
"total" => "Total",
|
||||
"total_lbp" => "Total LBP",
|
||||
"total_tax_exclusive" => "Tax excluded",
|
||||
"transaction_failed" => "Sales Transaction failed.",
|
||||
"unable_to_add_item" => "Item add to Sale failed",
|
||||
@@ -236,5 +230,3 @@ return [
|
||||
"work_order_sent" => "Work Order sent to",
|
||||
"work_order_unsent" => "Work Order failed to be sent to",
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ return [
|
||||
"cost_price_required" => "Precio al Por Mayor es un campo requerido.",
|
||||
"count" => "Actualizar Inventario",
|
||||
"csv_import_failed" => "Falló la importación de Hoja de Cálculo",
|
||||
"csv_import_invalid_location" => "Se encontraron ubicaciones de stock no válidas: {0}. Solo se permiten ubicaciones de stock válidas.",
|
||||
"csv_import_invalid_location" => "Ubicación(es) de stock inválida(s) encontrada(s): {0}. Solo ubicaciones de stock válidas son permitidas.",
|
||||
"csv_import_nodata_wrongformat" => "El archivo subido no tiene datos o el formato es incorrecto.",
|
||||
"csv_import_partially_failed" => "Hubo {0} falla(s) en la importación de producto(s) en la(s) línea(s): {1}. Ninguna fila ha sido importada.",
|
||||
"csv_import_success" => "Se importaron los articulos exitosamente.",
|
||||
|
||||
@@ -9,7 +9,6 @@ return [
|
||||
"amount_due" => "Monto Adeudado",
|
||||
"amount_tendered" => "Cantidad Recibida",
|
||||
"authorized_signature" => "Firma Autorizada",
|
||||
"bank_transfer" => "Transferencia Bancaria",
|
||||
"cancel_sale" => "Cancelar Venta",
|
||||
"cash" => "Efectivo",
|
||||
"cash_1" => "1",
|
||||
@@ -223,7 +222,6 @@ return [
|
||||
"update" => "Editar",
|
||||
"upi" => "PIN UPI",
|
||||
"visa" => "Tarjeta Visa",
|
||||
"wallet" => "Monedero",
|
||||
"wholesale" => "Precio al por mayor",
|
||||
"work_order" => "Orden trabajo",
|
||||
"work_order_number" => "Numero Orden Trabajo",
|
||||
|
||||
@@ -9,7 +9,6 @@ return [
|
||||
"amount_due" => "Monto de adeudo",
|
||||
"amount_tendered" => "Cantidad Recibida",
|
||||
"authorized_signature" => "Firma Autorizada",
|
||||
"bank_transfer" => "Transferencia Bancaria",
|
||||
"cancel_sale" => "Cancelar",
|
||||
"cash" => "Efectivo",
|
||||
"cash_1" => "",
|
||||
@@ -223,7 +222,6 @@ return [
|
||||
"update" => "Actualizar",
|
||||
"upi" => "UPI",
|
||||
"visa" => "",
|
||||
"wallet" => "Monedero",
|
||||
"wholesale" => "",
|
||||
"work_order" => "Orden de trabajo",
|
||||
"work_order_number" => "Número de orden de trabajo",
|
||||
|
||||
@@ -9,7 +9,6 @@ return [
|
||||
"amount_due" => "Montant à Payer",
|
||||
"amount_tendered" => "Montant Présenté",
|
||||
"authorized_signature" => "Signature autorisée",
|
||||
"bank_transfer" => "Virement Bancaire",
|
||||
"cancel_sale" => "Annuler la Vente",
|
||||
"cash" => "Espèce",
|
||||
"cash_1" => "",
|
||||
@@ -223,7 +222,6 @@ return [
|
||||
"update" => "Éditer",
|
||||
"upi" => "UPI",
|
||||
"visa" => "",
|
||||
"wallet" => "Portefeuille",
|
||||
"wholesale" => "",
|
||||
"work_order" => "Commande de travail",
|
||||
"work_order_number" => "Numéro de commande",
|
||||
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
"february" => "",
|
||||
"march" => "",
|
||||
"april" => "",
|
||||
"may" => "",
|
||||
"mayl" => "",
|
||||
"june" => "",
|
||||
"july" => "",
|
||||
"august" => "",
|
||||
@@ -46,4 +46,4 @@ return [
|
||||
"october" => "",
|
||||
"november" => "",
|
||||
"december" => "",
|
||||
];
|
||||
];
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
"february" => "",
|
||||
"march" => "",
|
||||
"april" => "",
|
||||
"may" => "",
|
||||
"mayl" => "",
|
||||
"june" => "",
|
||||
"july" => "",
|
||||
"august" => "",
|
||||
@@ -46,4 +46,4 @@ return [
|
||||
"october" => "",
|
||||
"november" => "",
|
||||
"december" => "",
|
||||
];
|
||||
];
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
"february" => "ഫെബ്രുവരി",
|
||||
"march" => "മാർച്ച്",
|
||||
"april" => "ഏപ്രിൽ",
|
||||
"may" => "മേയ്",
|
||||
"mayl" => "മേയ്",
|
||||
"june" => "ജൂൺ",
|
||||
"july" => "ജൂലൈ",
|
||||
"august" => "ആഗസ്റ്റ്",
|
||||
@@ -46,4 +46,4 @@ return [
|
||||
"october" => "ഒക്ടോബർ",
|
||||
"november" => "നവംബർ",
|
||||
"december" => "ഡിസംബർ",
|
||||
];
|
||||
];
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
"february" => "Februar",
|
||||
"march" => "Mars",
|
||||
"april" => "April",
|
||||
"may" => "Mai",
|
||||
"mayl" => "Mai",
|
||||
"june" => "Juni",
|
||||
"july" => "Juli",
|
||||
"august" => "August",
|
||||
@@ -46,4 +46,4 @@ return [
|
||||
"october" => "Oktober",
|
||||
"november" => "November",
|
||||
"december" => "Desember",
|
||||
];
|
||||
];
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'all' => "ทั้งหมด",
|
||||
'columns' => "คอลัมน์",
|
||||
'hide_show_pagination' => "ซ่อน/แสดง รายการหน้า",
|
||||
'loading' => "กำลังดำเนินการ รอสักครู่ ...",
|
||||
'page_from_to' => "แสดง {0} ถึง {1} จาก {2} รายการ",
|
||||
'refresh' => "Refresh ข้อมูล",
|
||||
'rows_per_page' => "{0} รายการ/หน้า",
|
||||
'toggle' => "ซ่อน/แสดง",
|
||||
"all" => "ทั้งหมด",
|
||||
"columns" => "คอลัมน์",
|
||||
"hide_show_pagination" => "ซ่อน/แสดง รายการหน้า",
|
||||
"loading" => "กำลังดำเนินการ รอสักครู่",
|
||||
"page_from_to" => "แสดง {0} ถึง {1} จาก {2} รายการ",
|
||||
"refresh" => "Refresh ข้อมูล",
|
||||
"rows_per_page" => "{0} รายการ/หน้า",
|
||||
"toggle" => "ซ่อน/แสดง",
|
||||
];
|
||||
|
||||
@@ -9,9 +9,7 @@ return [
|
||||
"login" => "ลงชื่อเข้าใช้",
|
||||
"logout" => "ออกจากระบบ",
|
||||
"migration_needed" => "การย้ายฐานข้อมูลไปยัง {0} จะเริ่มต้นหลังจากเข้าสู่ระบบ",
|
||||
"migration_required" => "จําเป็นต้องมีการปรับปรุงฐานข้อมูล",
|
||||
"migration_auth_message" => "ผู้ดูแลระบบจำเป็นต้องมีสิทธิ์ในการปรับปรุงฐานข้อมูลเวอร์ชั่น {0} กรุณาเข้าระบบเพื่อดำเนินการต่อ",
|
||||
"migration_complete_redirect" => "ทำการปรับปรุงฐานข้อมูลเรียบร้อย กำลังดำเนินการไปหน้าเข้าสู่ระบบ ...",
|
||||
"migration_required" => "",
|
||||
"migration_auth_message" => "",
|
||||
"migration_initializing" => "",
|
||||
"migration_running" => "",
|
||||
@@ -19,6 +17,7 @@ return [
|
||||
"migration_complete_login" => "",
|
||||
"migration_failed" => "",
|
||||
"migration_error_connection" => "",
|
||||
"migration_complete_redirect" => "",
|
||||
"password" => "รหัสผ่าน",
|
||||
"required_username" => "จำเป็นต้องระบุชื่อผู้ใช้งาน",
|
||||
"username" => "ชื่อผู้ใช้",
|
||||
|
||||
@@ -1,232 +1,232 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'customers_available_points' => "คะแนนที่มี",
|
||||
'rewards_package' => "คะแนนสะสม",
|
||||
'rewards_remaining_balance' => "คะแนนสะสมคงเหลือ ",
|
||||
'account_number' => "บัญชี #",
|
||||
'add_payment' => "เพิ่มบิล",
|
||||
'amount_due' => "ยอดค้างชำระ",
|
||||
'amount_tendered' => "ชำระเข้ามา",
|
||||
'authorized_signature' => "ลายเซ็นผู้มีอำนาจ",
|
||||
'cancel_sale' => "ยกเลิกการขาย",
|
||||
'cash' => "เงินสด",
|
||||
'cash_1' => "",
|
||||
'cash_2' => "",
|
||||
'cash_3' => "",
|
||||
'cash_4' => "",
|
||||
'cash_adjustment' => "การปรับเงินสดขาย",
|
||||
'cash_deposit' => "ฝากเงินสด",
|
||||
'cash_filter' => "เงินสด",
|
||||
'change_due' => "เงินทอน",
|
||||
'change_price' => "เปลี่ยนราคาขาย",
|
||||
'check' => "โอนเงิน/พร้อมเพย์/เช็ค",
|
||||
'check_balance' => "เช็คยอดคงเหลือ",
|
||||
'check_filter' => "ตรวจสอบ",
|
||||
'close' => "",
|
||||
'comment' => "หมายเหตุ",
|
||||
'comments' => "หมายเหตุ",
|
||||
'company_name' => "",
|
||||
'complete' => "",
|
||||
'complete_sale' => "จบการขาย",
|
||||
'confirm_cancel_sale' => "แน่ใจหรือไม่ที่จะล้างการขายนี้? ทุกรายการจะถูกลบทั้งหมด",
|
||||
'confirm_delete' => "โปรดยืนยันการลบรายการขายที่เลือกไว้ ?",
|
||||
'confirm_restore' => "คุณแน่ใจหรือไม่ว่าต้องการยกเลิกการขายที่เลือกไว้?",
|
||||
'credit' => "เครดิตการ์ด",
|
||||
'credit_deposit' => "เงินฝากเครดิต",
|
||||
'credit_filter' => "บัตรเครติด",
|
||||
'current_table' => "",
|
||||
'customer' => "ลูกค้า",
|
||||
'customer_address' => "Customer Address",
|
||||
'customer_discount' => "ส่วนลด",
|
||||
'customer_email' => "Customer Email",
|
||||
'customer_location' => "Customer Location",
|
||||
'customer_mailchimp_status' => "สถานะของระบบส่งเมล์เมล์ชิม",
|
||||
'customer_optional' => "(ต้องระบุวันที่ชำระเงิน)",
|
||||
'customer_required' => "(ต้องระบุ)",
|
||||
'customer_total' => "Total",
|
||||
'customer_total_spent' => "",
|
||||
'daily_sales' => "",
|
||||
'date' => "วันที่ขาย",
|
||||
'date_range' => "ระหว่างวันที่",
|
||||
'date_required' => "กรุณากรอกวันที่ให้ถูกต้อง",
|
||||
'date_type' => "กรุณากรอกข้อมูลในช่องวันที่",
|
||||
'debit' => "บัตรประชารัฐ/เดบิตการ์ด",
|
||||
'debit_filter' => "",
|
||||
'delete' => "อนุญาตให้ลบ",
|
||||
'delete_confirmation' => "แน่ใจหรือไม่ที่จะลบรายการขายนี้, ลบแล้วไม่สามารถเรียกกลับคืนใด้",
|
||||
'delete_entire_sale' => "ลบการขายทั้งหมด",
|
||||
'delete_successful' => "คุณลบการขายสำเร็จ",
|
||||
'delete_unsuccessful' => "คุณลบการขายไม่สำเร็จ",
|
||||
'description_abbrv' => "รายละเอียด",
|
||||
'discard' => "ยกเลิก",
|
||||
'discard_quote' => "",
|
||||
'discount' => "ส่วนลด %",
|
||||
'discount_included' => "% ส่วนลด",
|
||||
'discount_short' => "%",
|
||||
'due' => "วันครบกำหนด",
|
||||
'due_filter' => "วันที่ครบกำหนด",
|
||||
'edit' => "แก้ไข",
|
||||
'edit_item' => "แก้ไขสินค้า",
|
||||
'edit_sale' => "แก้ไขการขาย",
|
||||
'email_receipt' => "อีเมลบิล",
|
||||
'employee' => "พนักงาน",
|
||||
'entry' => "การนำเข้า",
|
||||
'error_editing_item' => "แก้ไขสินค้าล้มเหลว",
|
||||
'negative_price_invalid' => "ราคาไม่สามารถเป็นค่าติดลบได้",
|
||||
'negative_quantity_invalid' => "จำนวนไม่สามารถเป็นค่าติดลบได้",
|
||||
'negative_discount_invalid' => "ส่วนลดไม่สามารถเป็นค่าติดลบได้",
|
||||
'discount_percent_exceeds_100' => "ส่วนลดเปอร์เซ็นต์มีค่าได้ไม่เกิน 100%",
|
||||
'discount_exceeds_item_total' => "ส่วนลดต้องไม่เกินจำนวนรายการขายทั้งหมด",
|
||||
'negative_total_invalid' => "",
|
||||
'find_or_scan_item' => "ค้นหาสินค้า",
|
||||
'find_or_scan_item_or_receipt' => "ค้นหา หรือ แสกนรายการ หรือ ใบเสร็จ",
|
||||
'giftcard' => "บัตรของขวัญ",
|
||||
'giftcard_balance' => "ยอดคงเหลือบัตรของขวัญ",
|
||||
'giftcard_filter' => "",
|
||||
'giftcard_number' => "เลขที่บัตรของขวัญ",
|
||||
'group_by_category' => "กลุ่มตามหมวดหมู่",
|
||||
'group_by_type' => "กลุ่มตามประเภท",
|
||||
'hsn' => "HSN",
|
||||
'id' => "เลขที่ขาย",
|
||||
'include_prices' => "รวมในราคา?",
|
||||
'invoice' => "ใบแจ้งหนี้",
|
||||
'invoice_confirm' => "ใบแจ้งหนี้นี้จะถูกส่งไปที่",
|
||||
'invoice_enable' => "เลขที่ใบแจ้งหนี้",
|
||||
'invoice_filter' => "ใบแจ้งหนี้",
|
||||
'invoice_no_email' => "ลูกค้ารายนี้ไม่มีที่อยู่อีเมล",
|
||||
'invoice_number' => "เลขใบแจ้งหนี้ #",
|
||||
'invoice_number_duplicate' => "ใบแจ้งหนี้หมายเลข {0} จะต้องไม่ซ้ำกัน",
|
||||
'invoice_sent' => "ส่งใบแจ้งหนี้ไปที่",
|
||||
'invoice_total' => "ยอดรวมในใบแจ้งหนี้",
|
||||
'invoice_type_custom_invoice' => "ใบแจ้งหนี้ที่กำหนดเอง (custom_invoice.php)",
|
||||
'invoice_type_custom_tax_invoice' => "ใบกำกับภาษีที่กำหนดเอง (custom_tax_invoice.php)",
|
||||
'invoice_type_invoice' => "ใบแจ้งหนี้ (invoice.php)",
|
||||
'invoice_type_tax_invoice' => "ใบกำกับภาษี (tax_invoice.php)",
|
||||
'invoice_unsent' => "ไม่สามารถส่งใบแจ้งหนี้ถึง",
|
||||
'invoice_update' => "คำนวณใหม่",
|
||||
'item_insufficient_of_stock' => "จำนวนสินค้าไม่เพียงพอ",
|
||||
'item_name' => "ชื่อสินค้า",
|
||||
'item_number' => "สินค้า #",
|
||||
'item_out_of_stock' => "สินค้าจำหน่ายหมด",
|
||||
'key_browser' => "ความช่วยเหลือ",
|
||||
'key_cancel' => "ยกเลิกใบเสนอราคา/ใบแจ้งหนี้ /ใบการขาย นี้",
|
||||
'key_customer_search' => "ค้นหาลูกค้า",
|
||||
'key_finish_quote' => "จบใบเสนอราคา/ใบแจ้งหนี้โดยไม่ต้องชำระเงิน",
|
||||
'key_finish_sale' => "เพิ่มการชำระเงินและใบแจ้งหนี้ /ใบรายการขาย",
|
||||
'key_full' => "เปิดแบบเต็มหน้าจอ",
|
||||
'key_function' => "ฟังก์ชั่น",
|
||||
'key_help' => "คำสั่งลัดงานขาย",
|
||||
'key_help_modal' => "เปิดหน้าต่างคำสั่งลัดงานขาย",
|
||||
'key_in' => "ขยายเข้า",
|
||||
'key_item_search' => "ค้นหารายการขาย",
|
||||
'key_out' => "ขยายออก",
|
||||
'key_payment' => "เพิ่มการชำระเงิน",
|
||||
'key_print' => "พิมพ์หน้านี้",
|
||||
'key_restore' => "คืนการแสดงผลแบบดั้งเดิม/ขยาย",
|
||||
'key_search' => "ค้นหาตารางรายงาน",
|
||||
'key_suspend' => "พักรายการขายปัจจุบัน",
|
||||
'key_suspended' => "แสดงรายการขายที่พักไว้",
|
||||
'key_system' => "ทางลัดระบบ",
|
||||
'key_tendered' => "แก้ไขจำนวนเงินรับมา",
|
||||
'key_title' => "ทางลัดคียบอร์ดงานขาย",
|
||||
'mc' => "",
|
||||
'mode' => "รูปแบบการลงทะเบียน",
|
||||
'must_enter_numeric' => "จำนวนที่ถุกประมูลต้องใส่ข้อมุลที่เปนตัวเลข",
|
||||
'must_enter_numeric_giftcard' => "เลขที่บัตรของขวัญ ต้องใส่ตัวเลขเท่านั้น",
|
||||
'new_customer' => "ลูกค้าใหม่",
|
||||
'new_item' => "สินค้าใหม่",
|
||||
'no_description' => "ไม่ระบุรายละเอียด",
|
||||
'no_filter' => "ทั้งหมด",
|
||||
'no_items_in_cart' => "ไม่พบสินค้าในตระกร้า",
|
||||
'no_sales_to_display' => "ไม่มีการขายที่จะแสดง",
|
||||
'none_selected' => "คุณยังไม่ได้เลือกการขายที่จะลบ",
|
||||
'nontaxed_ind' => " . ",
|
||||
'not_authorized' => "การกระทำนี้ไม่ได้รับอนุญาต",
|
||||
'one_or_multiple' => "การขาย",
|
||||
'payment' => "รูปแบบชำระเงิน",
|
||||
'payment_amount' => "จำนวน",
|
||||
'payment_not_cover_total' => "จำนวนเงินที่ชำระต้องมากกว่าหรือเท่ากับยอดรวม",
|
||||
'payment_type' => "ชำระโดย",
|
||||
'payments' => "",
|
||||
'payments_total' => "ยอดชำระแล้ว",
|
||||
'price' => "ราคา",
|
||||
'print_after_sale' => "พิมพ์บิลหลังการขาย",
|
||||
'quantity' => "จำนวน",
|
||||
'quantity_less_than_reorder_level' => "คำเตือน ถ้าจำนวนของไม่เพียงพอกับความต้องการหรือไม่ตรงกับยอดในบันชี ก็สามารถทำการขายได้ แต่ต้องเชคปริมานสินค้าคงคลัง",
|
||||
'quantity_less_than_zero' => "คำเตือน: ถ้าจำนวนของไม่เพียงพอกับความต้องการหรือไม่ตรงกับยอดในบัญชี ก็สามารถทำการขายได้ แต่ต้องตรวจสอบปริมาญสินค้าคงคลังก่อน",
|
||||
'quantity_of_items' => "ปริมาณของ {0} รายการ",
|
||||
'quote' => "ใบเสนอราคา",
|
||||
'quote_number' => "หมายเลขอ้างอิง",
|
||||
'quote_number_duplicate' => "หมายเลขอ้างอิงต้องไม่ซ้ำกัน",
|
||||
'quote_sent' => "ส่งการอ้างอิงถึง",
|
||||
'quote_unsent' => "ส่งการอ้างอิงถึงผิดพลาด",
|
||||
'receipt' => "บิลขาย",
|
||||
'receipt_no_email' => "ลูกค้านี้ไม่มีที่อยู่อีเมล์",
|
||||
'receipt_number' => "จุดขาย#",
|
||||
'receipt_sent' => "ส่งใบเสร็จไปที่",
|
||||
'receipt_unsent' => "ไม่สามารถส่งใบเสร็จไปที่",
|
||||
'refund' => "ประเภทการยกเลิกการขาย",
|
||||
'register' => "ลงทะเบียนขาย",
|
||||
'remove_customer' => "ลบลูกค้า",
|
||||
'remove_discount' => "",
|
||||
'return' => "คืน",
|
||||
'rewards' => "คะแนนสะสม",
|
||||
'rewards_balance' => "คะแนนสะสมคงเหลือ",
|
||||
'sale' => "ขาย",
|
||||
'sale_by_invoice' => "การขายโดยใบแจ้งหนี้",
|
||||
'sale_for_customer' => "ลูกค้า:",
|
||||
'sale_time' => "เวลา",
|
||||
'sales_tax' => "ภาษีการขาย",
|
||||
'sales_total' => "",
|
||||
'select_customer' => "เลือกลูกค้า (Optional)",
|
||||
'send_invoice' => "ส่งใบแจ้งหนี้",
|
||||
'send_quote' => "ส่งใบเสนอราคา",
|
||||
'send_receipt' => "ส่งใบเสร็จ",
|
||||
'send_work_order' => "ส่งคำสั่งงาน",
|
||||
'serial' => "หมายเลขซีเรียล",
|
||||
'service_charge' => "",
|
||||
'show_due' => "",
|
||||
'show_invoice' => "ใบแจ้งหนี้",
|
||||
'show_receipt' => "ใบเสร็จ",
|
||||
'start_typing_customer_name' => "เริ่มต้นพิมพ์ชื่อลูกค้า...",
|
||||
'start_typing_item_name' => "เริ่มต้นพิมพ์ชื่อสินค้า หรือ สแกนบาร์โค๊ด...",
|
||||
'stock' => "คลังสินค้า",
|
||||
'stock_location' => "ที่เก็บ",
|
||||
'sub_total' => "ยอดรวมย่อย",
|
||||
'successfully_deleted' => "ลบการขายสมยูรณ์",
|
||||
'successfully_restored' => "คุณกู้คืนสำเร็จแล้ว",
|
||||
'successfully_suspended_sale' => "การขายของคุณถูกระงับเรียบร้อย",
|
||||
'successfully_updated' => "อัพเดทการขายสมบูรณ์",
|
||||
'suspend_sale' => "พักรายการ",
|
||||
'suspended_doc_id' => "รหัสเอกสาร",
|
||||
'suspended_sale_id' => "รหัสการขายที่ถูกพัก",
|
||||
'suspended_sales' => "การขายที่พักไว้",
|
||||
'table' => "โต๊ะ",
|
||||
'takings' => "การขายประจำวัน",
|
||||
'tax' => "ภาษี",
|
||||
'tax_id' => "รหัสภาษี",
|
||||
'tax_invoice' => "ใบกำกับภาษี",
|
||||
'tax_percent' => "ภาษี %",
|
||||
'taxed_ind' => "ภ",
|
||||
'total' => "ยอดรวม",
|
||||
'total_tax_exclusive' => "ยอดไม่รวมภาษี",
|
||||
'transaction_failed' => "การดำเนินการขายล้มเหลว",
|
||||
'unable_to_add_item' => "เพิ่มรายการไปยังการขายล้มเหลว",
|
||||
'unsuccessfully_deleted' => "ลบการขายไม่สำเร็จ",
|
||||
'unsuccessfully_restored' => "การคืนค่ารายการขายล้มเหลว",
|
||||
'unsuccessfully_suspended_sale' => "การขายของคุณถูกระงับเรียบร้อย",
|
||||
'unsuccessfully_updated' => "อัพเดทการขายไม่สมบูรณ์",
|
||||
'unsuspend' => "ยกเลิกการระงับ",
|
||||
'unsuspend_and_delete' => "ยกเลิกการระงับ และ ลบ",
|
||||
'update' => "แก้ไข",
|
||||
'upi' => "ยูพีไอ",
|
||||
'visa' => "",
|
||||
'wholesale' => "",
|
||||
'work_order' => "คำสั่งงาน",
|
||||
'work_order_number' => "หมายเลขคำสั่งงาน",
|
||||
'work_order_number_duplicate' => "หมายเลขคำสั่งงานต้องไม่ซ้ำกัน",
|
||||
'work_order_sent' => "คำสั่งงานส่งถึง",
|
||||
'work_order_unsent' => "ส่งคำสั่งงานล้มเหลว",
|
||||
'selected_customer' => "ลูกค้าที่เลือก",
|
||||
"customers_available_points" => "คะแนนที่มี",
|
||||
"rewards_package" => "คะแนนสะสม",
|
||||
"rewards_remaining_balance" => "คะแนนสะสมคงเหลือ ",
|
||||
"account_number" => "บัญชี #",
|
||||
"add_payment" => "เพิ่มบิล",
|
||||
"amount_due" => "ยอดค้างชำระ",
|
||||
"amount_tendered" => "ชำระเข้ามา",
|
||||
"authorized_signature" => "ลายเซ็นผู้มีอำนาจ",
|
||||
"cancel_sale" => "ยกเลิกการขาย",
|
||||
"cash" => "เงินสด",
|
||||
"cash_1" => "",
|
||||
"cash_2" => "",
|
||||
"cash_3" => "",
|
||||
"cash_4" => "",
|
||||
"cash_adjustment" => "การปรับเงินสดขาย",
|
||||
"cash_deposit" => "ฝากเงินสด",
|
||||
"cash_filter" => "เงินสด",
|
||||
"change_due" => "เงินทอน",
|
||||
"change_price" => "เปลี่ยนราคาขาย",
|
||||
"check" => "โอนเงิน/พร้อมเพย์/เช็ค",
|
||||
"check_balance" => "เช็คยอดคงเหลือ",
|
||||
"check_filter" => "ตรวจสอบ",
|
||||
"close" => "",
|
||||
"comment" => "หมายเหตุ",
|
||||
"comments" => "หมายเหตุ",
|
||||
"company_name" => "",
|
||||
"complete" => "",
|
||||
"complete_sale" => "จบการขาย",
|
||||
"confirm_cancel_sale" => "แน่ใจหรือไม่ที่จะล้างการขายนี้? ทุกรายการจะถูกลบทั้งหมด",
|
||||
"confirm_delete" => "โปรดยืนยันการลบรายการขายที่เลือกไว้ ?",
|
||||
"confirm_restore" => "คุณแน่ใจหรือไม่ว่าต้องการยกเลิกการขายที่เลือกไว้?",
|
||||
"credit" => "เครดิตการ์ด",
|
||||
"credit_deposit" => "เงินฝากเครดิต",
|
||||
"credit_filter" => "บัตรเครติด",
|
||||
"current_table" => "",
|
||||
"customer" => "ลูกค้า",
|
||||
"customer_address" => "Customer Address",
|
||||
"customer_discount" => "ส่วนลด",
|
||||
"customer_email" => "Customer Email",
|
||||
"customer_location" => "Customer Location",
|
||||
"customer_mailchimp_status" => "สถานะของระบบส่งเมล์เมล์ชิม",
|
||||
"customer_optional" => "(ต้องระบุวันที่ชำระเงิน)",
|
||||
"customer_required" => "(ต้องระบุ)",
|
||||
"customer_total" => "Total",
|
||||
"customer_total_spent" => "",
|
||||
"daily_sales" => "",
|
||||
"date" => "วันที่ขาย",
|
||||
"date_range" => "ระหว่างวันที่",
|
||||
"date_required" => "กรุณากรอกวันที่ให้ถูกต้อง",
|
||||
"date_type" => "กรุณากรอกข้อมูลในช่องวันที่",
|
||||
"debit" => "บัตรประชารัฐ/เดบิตการ์ด",
|
||||
"debit_filter" => "",
|
||||
"delete" => "อนุญาตให้ลบ",
|
||||
"delete_confirmation" => "แน่ใจหรือไม่ที่จะลบรายการขายนี้, ลบแล้วไม่สามารถเรียกกลับคืนใด้",
|
||||
"delete_entire_sale" => "ลบการขายทั้งหมด",
|
||||
"delete_successful" => "คุณลบการขายสำเร็จ",
|
||||
"delete_unsuccessful" => "คุณลบการขายไม่สำเร็จ",
|
||||
"description_abbrv" => "รายละเอียด",
|
||||
"discard" => "ยกเลิก",
|
||||
"discard_quote" => "",
|
||||
"discount" => "ส่วนลด %",
|
||||
"discount_included" => "% ส่วนลด",
|
||||
"discount_short" => "%",
|
||||
"due" => "วันครบกำหนด",
|
||||
"due_filter" => "วันที่ครบกำหนด",
|
||||
"edit" => "แก้ไข",
|
||||
"edit_item" => "แก้ไขสินค้า",
|
||||
"edit_sale" => "แก้ไขการขาย",
|
||||
"email_receipt" => "อีเมลบิล",
|
||||
"employee" => "พนักงาน",
|
||||
"entry" => "การนำเข้า",
|
||||
"error_editing_item" => "แก้ไขสินค้าล้มเหลว",
|
||||
"negative_price_invalid" => "",
|
||||
"negative_quantity_invalid" => "",
|
||||
"negative_discount_invalid" => "",
|
||||
"discount_percent_exceeds_100" => "",
|
||||
"discount_exceeds_item_total" => "",
|
||||
"negative_total_invalid" => "",
|
||||
"find_or_scan_item" => "ค้นหาสินค้า",
|
||||
"find_or_scan_item_or_receipt" => "ค้นหา หรือ แสกนรายการ หรือ ใบเสร็จ",
|
||||
"giftcard" => "บัตรของขวัญ",
|
||||
"giftcard_balance" => "ยอดคงเหลือบัตรของขวัญ",
|
||||
"giftcard_filter" => "",
|
||||
"giftcard_number" => "เลขที่บัตรของขวัญ",
|
||||
"group_by_category" => "กลุ่มตามหมวดหมู่",
|
||||
"group_by_type" => "กลุ่มตามประเภท",
|
||||
"hsn" => "HSN",
|
||||
"id" => "เลขที่ขาย",
|
||||
"include_prices" => "รวมในราคา?",
|
||||
"invoice" => "ใบแจ้งหนี้",
|
||||
"invoice_confirm" => "ใบแจ้งหนี้นี้จะถูกส่งไปที่",
|
||||
"invoice_enable" => "เลขที่ใบแจ้งหนี้",
|
||||
"invoice_filter" => "ใบแจ้งหนี้",
|
||||
"invoice_no_email" => "ลูกค้ารายนี้ไม่มีที่อยู่อีเมล",
|
||||
"invoice_number" => "เลขใบแจ้งหนี้ #",
|
||||
"invoice_number_duplicate" => "ใบแจ้งหนี้หมายเลข {0} จะต้องไม่ซ้ำกัน",
|
||||
"invoice_sent" => "ส่งใบแจ้งหนี้ไปที่",
|
||||
"invoice_total" => "ยอดรวมในใบแจ้งหนี้",
|
||||
"invoice_type_custom_invoice" => "ใบแจ้งหนี้ที่กำหนดเอง (custom_invoice.php)",
|
||||
"invoice_type_custom_tax_invoice" => "ใบกำกับภาษีที่กำหนดเอง (custom_tax_invoice.php)",
|
||||
"invoice_type_invoice" => "ใบแจ้งหนี้ (invoice.php)",
|
||||
"invoice_type_tax_invoice" => "ใบกำกับภาษี (tax_invoice.php)",
|
||||
"invoice_unsent" => "ไม่สามารถส่งใบแจ้งหนี้ถึง",
|
||||
"invoice_update" => "คำนวณใหม่",
|
||||
"item_insufficient_of_stock" => "จำนวนสินค้าไม่เพียงพอ",
|
||||
"item_name" => "ชื่อสินค้า",
|
||||
"item_number" => "สินค้า #",
|
||||
"item_out_of_stock" => "สินค้าจำหน่ายหมด",
|
||||
"key_browser" => "ความช่วยเหลือ",
|
||||
"key_cancel" => "ยกเลิกใบเสนอราคา/ใบแจ้งหนี้ /ใบการขาย นี้",
|
||||
"key_customer_search" => "ค้นหาลูกค้า",
|
||||
"key_finish_quote" => "จบใบเสนอราคา/ใบแจ้งหนี้โดยไม่ต้องชำระเงิน",
|
||||
"key_finish_sale" => "เพิ่มการชำระเงินและใบแจ้งหนี้ /ใบรายการขาย",
|
||||
"key_full" => "เปิดแบบเต็มหน้าจอ",
|
||||
"key_function" => "ฟังก์ชั่น",
|
||||
"key_help" => "คำสั่งลัดงานขาย",
|
||||
"key_help_modal" => "เปิดหน้าต่างคำสั่งลัดงานขาย",
|
||||
"key_in" => "ขยายเข้า",
|
||||
"key_item_search" => "ค้นหารายการขาย",
|
||||
"key_out" => "ขยายออก",
|
||||
"key_payment" => "เพิ่มการชำระเงิน",
|
||||
"key_print" => "พิมพ์หน้านี้",
|
||||
"key_restore" => "คืนการแสดงผลแบบดั้งเดิม/ขยาย",
|
||||
"key_search" => "ค้นหาตารางรายงาน",
|
||||
"key_suspend" => "พักรายการขายปัจจุบัน",
|
||||
"key_suspended" => "แสดงรายการขายที่พักไว้",
|
||||
"key_system" => "ทางลัดระบบ",
|
||||
"key_tendered" => "แก้ไขจำนวนเงินรับมา",
|
||||
"key_title" => "ทางลัดคียบอร์ดงานขาย",
|
||||
"mc" => "",
|
||||
"mode" => "รูปแบบการลงทะเบียน",
|
||||
"must_enter_numeric" => "จำนวนที่ถุกประมูลต้องใส่ข้อมุลที่เปนตัวเลข",
|
||||
"must_enter_numeric_giftcard" => "เลขที่บัตรของขวัญ ต้องใส่ตัวเลขเท่านั้น",
|
||||
"new_customer" => "ลูกค้าใหม่",
|
||||
"new_item" => "สินค้าใหม่",
|
||||
"no_description" => "ไม่ระบุรายละเอียด",
|
||||
"no_filter" => "ทั้งหมด",
|
||||
"no_items_in_cart" => "ไม่พบสินค้าในตระกร้า",
|
||||
"no_sales_to_display" => "ไม่มีการขายที่จะแสดง",
|
||||
"none_selected" => "คุณยังไม่ได้เลือกการขายที่จะลบ",
|
||||
"nontaxed_ind" => " . ",
|
||||
"not_authorized" => "การกระทำนี้ไม่ได้รับอนุญาต",
|
||||
"one_or_multiple" => "การขาย",
|
||||
"payment" => "รูปแบบชำระเงิน",
|
||||
"payment_amount" => "จำนวน",
|
||||
"payment_not_cover_total" => "จำนวนเงินที่ชำระต้องมากกว่าหรือเท่ากับยอดรวม",
|
||||
"payment_type" => "ชำระโดย",
|
||||
"payments" => "",
|
||||
"payments_total" => "ยอดชำระแล้ว",
|
||||
"price" => "ราคา",
|
||||
"print_after_sale" => "พิมพ์บิลหลังการขาย",
|
||||
"quantity" => "จำนวน",
|
||||
"quantity_less_than_reorder_level" => "คำเตือน ถ้าจำนวนของไม่เพียงพอกับความต้องการหรือไม่ตรงกับยอดในบันชี ก็สามารถทำการขายได้ แต่ต้องเชคปริมานสินค้าคงคลัง",
|
||||
"quantity_less_than_zero" => "คำเตือน: ถ้าจำนวนของไม่เพียงพอกับความต้องการหรือไม่ตรงกับยอดในบัญชี ก็สามารถทำการขายได้ แต่ต้องตรวจสอบปริมาญสินค้าคงคลังก่อน",
|
||||
"quantity_of_items" => "ปริมาณของ {0} รายการ",
|
||||
"quote" => "ใบเสนอราคา",
|
||||
"quote_number" => "หมายเลขอ้างอิง",
|
||||
"quote_number_duplicate" => "หมายเลขอ้างอิงต้องไม่ซ้ำกัน",
|
||||
"quote_sent" => "ส่งการอ้างอิงถึง",
|
||||
"quote_unsent" => "ส่งการอ้างอิงถึงผิดพลาด",
|
||||
"receipt" => "บิลขาย",
|
||||
"receipt_no_email" => "ลูกค้านี้ไม่มีที่อยู่อีเมล์",
|
||||
"receipt_number" => "จุดขาย#",
|
||||
"receipt_sent" => "ส่งใบเสร็จไปที่",
|
||||
"receipt_unsent" => "ไม่สามารถส่งใบเสร็จไปที่",
|
||||
"refund" => "ประเภทการยกเลิกการขาย",
|
||||
"register" => "ลงทะเบียนขาย",
|
||||
"remove_customer" => "ลบลูกค้า",
|
||||
"remove_discount" => "",
|
||||
"return" => "คืน",
|
||||
"rewards" => "คะแนนสะสม",
|
||||
"rewards_balance" => "คะแนนสะสมคงเหลือ",
|
||||
"sale" => "ขาย",
|
||||
"sale_by_invoice" => "การขายโดยใบแจ้งหนี้",
|
||||
"sale_for_customer" => "ลูกค้า:",
|
||||
"sale_time" => "เวลา",
|
||||
"sales_tax" => "ภาษีการขาย",
|
||||
"sales_total" => "",
|
||||
"select_customer" => "เลือกลูกค้า (Optional)",
|
||||
"send_invoice" => "ส่งใบแจ้งหนี้",
|
||||
"send_quote" => "ส่งใบเสนอราคา",
|
||||
"send_receipt" => "ส่งใบเสร็จ",
|
||||
"send_work_order" => "ส่งคำสั่งงาน",
|
||||
"serial" => "หมายเลขซีเรียล",
|
||||
"service_charge" => "",
|
||||
"show_due" => "",
|
||||
"show_invoice" => "ใบแจ้งหนี้",
|
||||
"show_receipt" => "ใบเสร็จ",
|
||||
"start_typing_customer_name" => "เริ่มต้นพิมพ์ชื่อลูกค้า...",
|
||||
"start_typing_item_name" => "เริ่มต้นพิมพ์ชื่อสินค้า หรือ สแกนบาร์โค๊ด...",
|
||||
"stock" => "คลังสินค้า",
|
||||
"stock_location" => "ที่เก็บ",
|
||||
"sub_total" => "ยอดรวมย่อย",
|
||||
"successfully_deleted" => "ลบการขายสมยูรณ์",
|
||||
"successfully_restored" => "คุณกู้คืนสำเร็จแล้ว",
|
||||
"successfully_suspended_sale" => "การขายของคุณถูกระงับเรียบร้อย",
|
||||
"successfully_updated" => "อัพเดทการขายสมบูรณ์",
|
||||
"suspend_sale" => "พักรายการ",
|
||||
"suspended_doc_id" => "รหัสเอกสาร",
|
||||
"suspended_sale_id" => "รหัสการขายที่ถูกพัก",
|
||||
"suspended_sales" => "การขายที่พักไว้",
|
||||
"table" => "โต๊ะ",
|
||||
"takings" => "การขายประจำวัน",
|
||||
"tax" => "ภาษี",
|
||||
"tax_id" => "รหัสภาษี",
|
||||
"tax_invoice" => "ใบกำกับภาษี",
|
||||
"tax_percent" => "ภาษี %",
|
||||
"taxed_ind" => "ภ",
|
||||
"total" => "ยอดรวม",
|
||||
"total_tax_exclusive" => "ยอดไม่รวมภาษี",
|
||||
"transaction_failed" => "การดำเนินการขายล้มเหลว",
|
||||
"unable_to_add_item" => "เพิ่มรายการไปยังการขายล้มเหลว",
|
||||
"unsuccessfully_deleted" => "ลบการขายไม่สำเร็จ",
|
||||
"unsuccessfully_restored" => "การคืนค่ารายการขายล้มเหลว",
|
||||
"unsuccessfully_suspended_sale" => "การขายของคุณถูกระงับเรียบร้อย",
|
||||
"unsuccessfully_updated" => "อัพเดทการขายไม่สมบูรณ์",
|
||||
"unsuspend" => "ยกเลิกการระงับ",
|
||||
"unsuspend_and_delete" => "ยกเลิกการระงับ และ ลบ",
|
||||
"update" => "แก้ไข",
|
||||
"upi" => "ยูพีไอ",
|
||||
"visa" => "",
|
||||
"wholesale" => "",
|
||||
"work_order" => "คำสั่งงาน",
|
||||
"work_order_number" => "หมายเลขคำสั่งงาน",
|
||||
"work_order_number_duplicate" => "หมายเลขคำสั่งงานต้องไม่ซ้ำกัน",
|
||||
"work_order_sent" => "คำสั่งงานส่งถึง",
|
||||
"work_order_unsent" => "ส่งคำสั่งงานล้มเหลว",
|
||||
"selected_customer" => "ลูกค้าที่เลือก",
|
||||
];
|
||||
|
||||
@@ -82,40 +82,4 @@ class Email_lib
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mime type of the company logo file.
|
||||
*
|
||||
* @return string Mime type or empty string if logo doesn't exist
|
||||
*/
|
||||
public function getLogoMimeType(): string
|
||||
{
|
||||
$logo_path = FCPATH . 'uploads/' . $this->config['company_logo'];
|
||||
|
||||
if (!empty($this->config['company_logo']) && file_exists($logo_path)) {
|
||||
$mimeType = mime_content_type($logo_path);
|
||||
return $mimeType !== false ? $mimeType : '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an img tag for the company logo to use in email templates.
|
||||
*
|
||||
* @return string HTML img tag with base64-encoded logo, or empty string if no logo
|
||||
*/
|
||||
public function buildLogoImgTag(): string
|
||||
{
|
||||
$mimeType = $this->getLogoMimeType();
|
||||
|
||||
if ($mimeType === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$logo_path = FCPATH . 'uploads/' . $this->config['company_logo'];
|
||||
$logo_data = base64_encode(file_get_contents($logo_path));
|
||||
|
||||
return '<img id="image" src="data:' . $mimeType . ';base64,' . $logo_data . '" alt="company_logo">';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Database\MigrationRunner;
|
||||
use Config\Database;
|
||||
use stdClass;
|
||||
@@ -43,9 +44,7 @@ class MY_Migration extends MigrationRunner
|
||||
$result = $builder->get()->getRow();
|
||||
return $result ? $result->version : 0;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Database not available yet (e.g. fresh install before schema).
|
||||
// Catches mysqli_sql_exception which is not a DatabaseException.
|
||||
} catch (DatabaseException $e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -77,9 +76,8 @@ class MY_Migration extends MigrationRunner
|
||||
$result = $builder->get()->getRow();
|
||||
return $result ? $result->version : false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Database not available yet (e.g. fresh install before schema).
|
||||
// Catches mysqli_sql_exception which is not a DatabaseException.
|
||||
} catch (DatabaseException $e) {
|
||||
// Database doesn't exist yet or connection failed
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -23,19 +23,6 @@ use ReflectionException;
|
||||
*/
|
||||
class Sale_lib
|
||||
{
|
||||
private const KEY_SHORTCUT_DEFAULTS = [
|
||||
'cancel' => ['value' => '27 | ESC', 'code' => 27, 'label' => 'ESC'],
|
||||
'items' => ['value' => '49 | ALT + 1', 'code' => 49, 'label' => 'ALT + 1'],
|
||||
'customers' => ['value' => '50 | ALT + 2', 'code' => 50, 'label' => 'ALT + 2'],
|
||||
'suspend' => ['value' => '51 | ALT + 3', 'code' => 51, 'label' => 'ALT + 3'],
|
||||
'suspended' => ['value' => '52 | ALT + 4', 'code' => 52, 'label' => 'ALT + 4'],
|
||||
'amount' => ['value' => '53 | ALT + 5', 'code' => 53, 'label' => 'ALT + 5'],
|
||||
'payment' => ['value' => '54 | ALT + 6', 'code' => 54, 'label' => 'ALT + 6'],
|
||||
'complete' => ['value' => '55 | ALT + 7', 'code' => 55, 'label' => 'ALT + 7'],
|
||||
'finish' => ['value' => '56 | ALT + 8', 'code' => 56, 'label' => 'ALT + 8'],
|
||||
'help' => ['value' => '57 | ALT + 9', 'code' => 57, 'label' => 'ALT + 9'],
|
||||
];
|
||||
|
||||
private Attribute $attribute;
|
||||
private Customer $customer;
|
||||
private Dinner_table $dinner_table;
|
||||
@@ -108,11 +95,6 @@ class Sale_lib
|
||||
'custom_tax_invoice'
|
||||
];
|
||||
|
||||
private const ALLOWED_RECEIPT_TEMPLATES = [
|
||||
'receipt_default',
|
||||
'receipt_short'
|
||||
];
|
||||
|
||||
public function get_invoice_type_options(): array
|
||||
{
|
||||
$invoice_types = [];
|
||||
@@ -123,54 +105,11 @@ class Sale_lib
|
||||
return $invoice_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available keyboard shortcut choices for the configuration screen.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getKeyShortcutsOptions(): array
|
||||
{
|
||||
$keyShortcuts = [];
|
||||
|
||||
foreach (self::KEY_SHORTCUT_DEFAULTS as $shortcut) {
|
||||
$keyShortcuts[$shortcut['value']] = $shortcut['label'];
|
||||
}
|
||||
|
||||
return $keyShortcuts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parsed shortcut bindings from app_config with sensible defaults.
|
||||
*
|
||||
* @return array<string, array{value:string,code:int,label:string}>
|
||||
*/
|
||||
public function getKeyShortcuts(): array
|
||||
{
|
||||
$keyboardShortcuts = [];
|
||||
|
||||
foreach (self::KEY_SHORTCUT_DEFAULTS as $name => $default) {
|
||||
$value = $this->config["key_$name"] ?? $default['value'];
|
||||
$parts = array_map('trim', explode('|', $value, 2));
|
||||
$keyboardShortcuts[$name] = [
|
||||
'value' => $value,
|
||||
'code' => (int)($parts[0] ?? $default['code']),
|
||||
'label' => $parts[1] ?? $default['label']
|
||||
];
|
||||
}
|
||||
|
||||
return $keyboardShortcuts;
|
||||
}
|
||||
|
||||
public static function isValidInvoiceType(string $invoice_type): bool
|
||||
{
|
||||
return in_array($invoice_type, self::ALLOWED_INVOICE_TYPES, true);
|
||||
}
|
||||
|
||||
public static function isValidReceiptTemplate(string $receipt_template): bool
|
||||
{
|
||||
return in_array($receipt_template, self::ALLOWED_RECEIPT_TEMPLATES, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -601,10 +601,6 @@ class Attribute extends Model
|
||||
*/
|
||||
public function saveAttributeLink(int $itemId, int $definitionId, int $attributeId): bool
|
||||
{
|
||||
if ($attributeId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalizedItemId = empty($itemId) ? null : $itemId;
|
||||
$normalizedAttributeId = empty($attributeId) ? null : $attributeId;
|
||||
|
||||
|
||||
@@ -65,10 +65,8 @@ class Item extends Model
|
||||
public function exists(string $item_id, bool $ignore_deleted = false, bool $deleted = false): bool
|
||||
{
|
||||
$builder = $this->db->table('items');
|
||||
$builder->groupStart();
|
||||
$builder->where('item_id', $item_id);
|
||||
$builder->orWhere('item_number', $item_id);
|
||||
$builder->groupEnd();
|
||||
|
||||
if (!$ignore_deleted) {
|
||||
$builder->where('deleted', $deleted);
|
||||
@@ -391,10 +389,9 @@ class Item extends Model
|
||||
public function get_item_id(string $item_number, bool $ignore_deleted = false, bool $deleted = false): bool|int
|
||||
{
|
||||
$builder = $this->db->table('items');
|
||||
$builder->groupStart();
|
||||
$builder->join('suppliers', 'suppliers.person_id = items.supplier_id', 'left');
|
||||
$builder->where('item_number', $item_number);
|
||||
$builder->orWhere('item_id', $item_number);
|
||||
$builder->groupEnd();
|
||||
|
||||
if (!$ignore_deleted) {
|
||||
$builder->where('items.deleted', $deleted);
|
||||
|
||||
@@ -294,9 +294,7 @@ class Receiving extends Model
|
||||
lang('Sales.check') => lang('Sales.check'),
|
||||
lang('Sales.debit') => lang('Sales.debit'),
|
||||
lang('Sales.credit') => lang('Sales.credit'),
|
||||
lang('Sales.due') => lang('Sales.due'),
|
||||
lang('Sales.bank_transfer') => lang('Sales.bank_transfer'),
|
||||
lang('Sales.wallet') => lang('Sales.wallet')
|
||||
lang('Sales.due') => lang('Sales.due')
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -33,16 +33,14 @@ class Summary_sales_taxes extends Summary_report
|
||||
* @param object $builder
|
||||
* @return void
|
||||
*/
|
||||
protected function _where(array $inputs, object &$builder): void
|
||||
protected function _where(array $inputs, object &$builder): void // TODO: hungarian notation
|
||||
{
|
||||
$builder->where('sales.sale_status', COMPLETED);
|
||||
|
||||
if (empty($this->config['date_or_time_format'])) {
|
||||
$builder->where('DATE(sales.sale_time) >=', $inputs['start_date']);
|
||||
$builder->where('DATE(sales.sale_time) <=', $inputs['end_date']);
|
||||
if (empty($this->config['date_or_time_format'])) { // TODO: Duplicated code
|
||||
$builder->where('DATE(sales.sale_time) BETWEEN ' . $this->db->escape($inputs['start_date']) . ' AND ' . $this->db->escape($inputs['end_date']));
|
||||
} else {
|
||||
$builder->where('sales.sale_time >=', $inputs['start_date']);
|
||||
$builder->where('sales.sale_time <=', $inputs['end_date']);
|
||||
$builder->where('sales.sale_time BETWEEN ' . $this->db->escape(rawurldecode($inputs['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($inputs['end_date'])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +53,9 @@ class Summary_sales_taxes extends Summary_report
|
||||
$builder = $this->db->table('sales_taxes');
|
||||
|
||||
if (empty($this->config['date_or_time_format'])) {
|
||||
$builder->where('DATE(sale_time) >=', $inputs['start_date']);
|
||||
$builder->where('DATE(sale_time) <=', $inputs['end_date']);
|
||||
$builder->where('DATE(sale_time) BETWEEN ' . $inputs['start_date'] . ' AND ' . $inputs['end_date']);
|
||||
} else {
|
||||
$builder->where('sale_time >=', $inputs['start_date']);
|
||||
$builder->where('sale_time <=', $inputs['end_date']);
|
||||
$builder->where('sale_time BETWEEN ' . $this->db->escape(rawurldecode($inputs['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($inputs['end_date'])));
|
||||
}
|
||||
|
||||
$builder->select('reporting_authority, jurisdiction_name, tax_category, tax_rate, SUM(sale_tax_amount) AS tax');
|
||||
|
||||
@@ -277,14 +277,6 @@ class Sale extends Model
|
||||
$builder->like('payment_type', lang('Sales.debit'));
|
||||
}
|
||||
|
||||
if ($filters['only_bank_transfer']) {
|
||||
$builder->like('payment_type', lang('Sales.bank_transfer'));
|
||||
}
|
||||
|
||||
if ($filters['only_wallet']) {
|
||||
$builder->like('payment_type', lang('Sales.wallet'));
|
||||
}
|
||||
|
||||
$builder->groupBy('payment_type');
|
||||
|
||||
$payments = $builder->get()->getResultArray();
|
||||
@@ -1517,13 +1509,5 @@ class Sale extends Model
|
||||
if ($filters['only_check']) {
|
||||
$builder->like('payments.payment_type', lang('Sales.check'));
|
||||
}
|
||||
|
||||
if ($filters['only_bank_transfer']) {
|
||||
$builder->like('payments.payment_type', lang('Sales.bank_transfer'));
|
||||
}
|
||||
|
||||
if ($filters['only_wallet']) {
|
||||
$builder->like('payments.payment_type', lang('Sales.wallet'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@
|
||||
<script type="text/javascript">
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
var values = [];
|
||||
var definition_id = <?= esc($definition_id, 'js') ?>;
|
||||
var is_new = definition_id == 0;
|
||||
const values = [];
|
||||
const definition_id = <?= esc($definition_id, 'js') ?>;
|
||||
const is_new = definition_id == 0;
|
||||
|
||||
var disable_definition_types = function() {
|
||||
var definition_type = $("#definition_type option:selected").text();
|
||||
const disable_definition_types = function() {
|
||||
const definition_type = $("#definition_type option:selected").text();
|
||||
|
||||
if (definition_type == "DATE" || (definition_type == "GROUP" && !is_new) || definition_type == "DECIMAL") {
|
||||
$('#definition_type').prop("disabled", true);
|
||||
@@ -121,7 +121,7 @@
|
||||
}
|
||||
disable_definition_types();
|
||||
|
||||
var disable_category_dropdown = function() {
|
||||
const disable_category_dropdown = function() {
|
||||
if (definition_id == -1) {
|
||||
$('#definition_name').prop("disabled", true);
|
||||
$('#definition_type').prop("disabled", true);
|
||||
@@ -131,11 +131,11 @@
|
||||
}
|
||||
disable_category_dropdown();
|
||||
|
||||
var show_hide_fields = function(event) {
|
||||
var is_dropdown = $('#definition_type').val() !== '1';
|
||||
var is_decimal = $('#definition_type').val() !== '2';
|
||||
var is_no_group = $('#definition_type').val() !== '0';
|
||||
var is_category_dropdown = definition_id == -1;
|
||||
const show_hide_fields = function(event) {
|
||||
const is_dropdown = $('#definition_type').val() !== '1';
|
||||
const is_decimal = $('#definition_type').val() !== '2';
|
||||
const is_no_group = $('#definition_type').val() !== '0';
|
||||
const is_category_dropdown = definition_id == -1;
|
||||
|
||||
$('#definition_value, #definition_list_group').parents('.form-group').toggleClass('hidden', is_dropdown);
|
||||
$('#definition_unit').parents('.form-group').toggleClass('hidden', is_decimal);
|
||||
@@ -150,12 +150,12 @@
|
||||
show_hide_fields();
|
||||
|
||||
$('.selectpicker').each(function() {
|
||||
var $selectpicker = $(this);
|
||||
const $selectpicker = $(this);
|
||||
$.fn.selectpicker.call($selectpicker, $selectpicker.data());
|
||||
});
|
||||
|
||||
var remove_attribute_value = function() {
|
||||
var value = $(this).parents("li").text();
|
||||
const remove_attribute_value = function() {
|
||||
const value = $(this).parents("li").text();
|
||||
|
||||
if (is_new) {
|
||||
values.splice($.inArray(value, values), 1);
|
||||
@@ -168,8 +168,8 @@
|
||||
$(this).parents("li").remove();
|
||||
};
|
||||
|
||||
var add_attribute_value = function(value) {
|
||||
var is_event = typeof(value) !== 'string';
|
||||
const add_attribute_value = function(value) {
|
||||
const is_event = typeof(value) !== 'string';
|
||||
|
||||
if ($("#definition_value").val().match(/(\||_)/g) != null) {
|
||||
return;
|
||||
@@ -206,7 +206,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
var definition_values = <?= json_encode(array_values($definition_values)) ?>;
|
||||
const definition_values = <?= json_encode(array_values($definition_values)) ?>;
|
||||
$.each(definition_values, function(index, element) {
|
||||
add_attribute_value(element);
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
(function() {
|
||||
<?= view('partial/datepicker_locale', ['format' => dateformat_bootstrap($config['dateformat'])]) ?>
|
||||
|
||||
var enable_delete = function() {
|
||||
const enable_delete = function() {
|
||||
$('.remove_attribute_btn').click(function() {
|
||||
$(this).parents('.form-group').remove();
|
||||
});
|
||||
@@ -113,7 +113,7 @@
|
||||
enable_delete();
|
||||
|
||||
$("input[name*='attribute_links']").change(function() {
|
||||
var definition_id = $(this).data('definition-id');
|
||||
const definition_id = $(this).data('definition-id');
|
||||
$("input[name='attribute_ids[" + definition_id + "]']").val('');
|
||||
}).autocomplete({
|
||||
source: function(request, response) {
|
||||
@@ -129,11 +129,11 @@
|
||||
delay: 10
|
||||
});
|
||||
|
||||
var definition_values = function() {
|
||||
var result = {};
|
||||
const definition_values = function() {
|
||||
const result = {};
|
||||
$("[name*='attribute_links'").each(function() {
|
||||
var definition_id = $(this).data('definition-id');
|
||||
var element = $(this);
|
||||
const definition_id = $(this).data('definition-id');
|
||||
const element = $(this);
|
||||
|
||||
// For checkboxes, use the visible checkbox, not the hidden input
|
||||
if (element.attr('type') === 'hidden' && element.siblings('input[type="checkbox"]').length > 0) {
|
||||
@@ -151,9 +151,9 @@
|
||||
return result;
|
||||
};
|
||||
|
||||
var refresh = function() {
|
||||
var definition_id = $("#definition_name option:selected").val();
|
||||
var attribute_values = definition_values();
|
||||
const refresh = function() {
|
||||
const definition_id = $("#definition_name option:selected").val();
|
||||
let attribute_values = definition_values();
|
||||
attribute_values[definition_id] = '';
|
||||
$('#attributes').load('<?= "items/attributes/$item_id" ?>', {
|
||||
'definition_ids': JSON.stringify(attribute_values)
|
||||
|
||||
@@ -11,34 +11,31 @@ $barcode_lib = new Barcode_lib();
|
||||
|
||||
<!doctype html>
|
||||
<html lang="<?= current_language_code() ?>">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= esc(lang('Items.generate_barcodes')) ?></title>
|
||||
<link rel="stylesheet" href="<?= esc(base_url('css/barcode_font.css'), 'url') ?>">
|
||||
<style>
|
||||
.barcode svg {
|
||||
height: <?= (int) $barcode_config['barcode_height'] ?>px;
|
||||
width: <?= (int) $barcode_config['barcode_width'] ?>px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="<?= esc('font_' . $barcode_lib->get_font_name($barcode_config['barcode_font']), 'attr') ?>" style="font-size: <?= (int) $barcode_config['barcode_font_size'] ?>px;">
|
||||
<table style="border-spacing: <?= (int) $barcode_config['barcode_page_cellspacing'] ?>px; width: <?= (int) $barcode_config['barcode_page_width'] ?>%;">
|
||||
<tr>
|
||||
<?php
|
||||
$count = 0;
|
||||
foreach ($items as $item) {
|
||||
if ($count % $barcode_config['barcode_num_in_row'] == 0 && $count != 0) {
|
||||
echo '</tr><tr>';
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= lang('Items.generate_barcodes') ?></title>
|
||||
<link rel="stylesheet" href="<?= base_url() ?>css/barcode_font.css">
|
||||
<style>
|
||||
.barcode svg {
|
||||
height: <?= $barcode_config['barcode_height'] ?>px;
|
||||
width: <?= $barcode_config['barcode_width'] ?>px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class=<?= 'font_' . $barcode_lib->get_font_name($barcode_config['barcode_font']) ?> style="font-size: <?= $barcode_config['barcode_font_size'] ?>px;">
|
||||
<table style="border-spacing: <?= $barcode_config['barcode_page_cellspacing'] ?>; width: <?= $barcode_config['barcode_page_width'] ?>%;">
|
||||
<tr>
|
||||
<?php
|
||||
$count = 0;
|
||||
foreach ($items as $item) {
|
||||
if ($count % $barcode_config['barcode_num_in_row'] == 0 && $count != 0) {
|
||||
echo '</tr><tr>';
|
||||
}
|
||||
echo '<td>' . $barcode_lib->display_barcode($item, $barcode_config) . '</td>';
|
||||
$count++;
|
||||
}
|
||||
echo '<td>' . $barcode_lib->display_barcode($item, $barcode_config) . '</td>';
|
||||
$count++;
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
?>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -308,7 +308,7 @@
|
||||
);
|
||||
});
|
||||
|
||||
var submit_form = function() {
|
||||
const submit_form = function() {
|
||||
$(this).ajaxSubmit({
|
||||
success: function(response) {
|
||||
dialog_support.hide();
|
||||
|
||||
@@ -204,7 +204,6 @@
|
||||
<?= form_label(lang('Config.barcode_number_in_row'), 'barcode_num_in_row', ['class' => 'control-label col-xs-2 required']) ?>
|
||||
<div class="col-xs-2">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'name' => 'barcode_num_in_row',
|
||||
'id' => 'barcode_num_in_row',
|
||||
'class' => 'form-control input-sm required',
|
||||
@@ -218,9 +217,6 @@
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'min' => '0',
|
||||
'max' => '100',
|
||||
'name' => 'barcode_page_width',
|
||||
'id' => 'barcode_page_width',
|
||||
'class' => 'form-control input-sm required',
|
||||
@@ -236,7 +232,6 @@
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'name' => 'barcode_page_cellspacing',
|
||||
'id' => 'barcode_page_cellspacing',
|
||||
'class' => 'form-control input-sm required',
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<?= form_dropdown(
|
||||
'protocol',
|
||||
[
|
||||
'mail' => 'Mail',
|
||||
'sendmail' => 'Sendmail',
|
||||
'smtp' => 'SMTP'
|
||||
'mail' => 'mail',
|
||||
'sendmail' => 'sendmail',
|
||||
'smtp' => 'smtp'
|
||||
],
|
||||
$config['protocol'],
|
||||
'class="form-control input-sm" id="protocol"'
|
||||
@@ -55,7 +55,6 @@
|
||||
<?= form_label(lang('Config.email_smtp_port'), 'smtp_port', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-2">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'name' => 'smtp_port',
|
||||
'id' => 'smtp_port',
|
||||
'class' => 'form-control input-sm',
|
||||
@@ -84,7 +83,6 @@
|
||||
<?= form_label(lang('Config.email_smtp_timeout'), 'smtp_timeout', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-2">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'name' => 'smtp_timeout',
|
||||
'id' => 'smtp_timeout',
|
||||
'class' => 'form-control input-sm',
|
||||
@@ -141,7 +139,7 @@
|
||||
<script type="text/javascript">
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
var check_protocol = function() {
|
||||
const check_protocol = function() {
|
||||
if ($('#protocol').val() == 'sendmail') {
|
||||
$('#mailpath').prop('disabled', false);
|
||||
$('#smtp_host, #smtp_user, #smtp_pass, #smtp_port, #smtp_timeout, #smtp_crypto').prop('disabled', true);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
) ?>
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<a href="<?= 'https://bootswatch.com/3/' . ('bootstrap' == ($config['theme']) ? 'default' : esc($config['theme'])) ?>" target="_blank" rel="noopener">
|
||||
<a href="<?= 'https://bootswatch.com/3/' . ('bootstrap' == ($config['theme']) ? 'default' : esc($config['theme'])) ?>" target="_blank" rel=”noopener”>
|
||||
<span><?= lang('Config.theme_preview') . ' ' . ucfirst(esc($config['theme'])) . ' ' ?></span>
|
||||
<span class="glyphicon glyphicon-new-window"></span>
|
||||
</a>
|
||||
@@ -130,17 +130,14 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<?= form_label(lang('Config.receiving_cost_price_method'), 'receiving_cost_price_method', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-3">
|
||||
<?= form_dropdown(
|
||||
'receiving_cost_price_method',
|
||||
[
|
||||
'average' => lang('Config.receiving_cost_price_method_average'),
|
||||
'new' => lang('Config.receiving_cost_price_method_new'),
|
||||
],
|
||||
(($config['receiving_cost_price_method'] ?? (($config['receiving_calculate_average_price'] ?? 1) ? 'average' : 'new'))),
|
||||
['id' => 'receiving_cost_price_method', 'class' => 'form-control']
|
||||
) ?>
|
||||
<?= form_label(lang('Config.receiving_calculate_average_price'), 'receiving_calculate_average_price', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-1">
|
||||
<?= form_checkbox([
|
||||
'name' => 'receiving_calculate_average_price',
|
||||
'id' => 'receiving_calculate_average_price',
|
||||
'value' => 'receiving_calculate_average_price',
|
||||
'checked' => $config['receiving_calculate_average_price'] == 1
|
||||
]) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -281,7 +278,7 @@
|
||||
'checked' => $config['gcaptcha_enable'] == 1
|
||||
]) ?>
|
||||
<label class="control-label">
|
||||
<a href="https://www.google.com/recaptcha/admin" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://www.google.com/recaptcha/admin" target="_blank">
|
||||
<span class="glyphicon glyphicon-info-sign" data-toggle="tooltip" data-placement="right" title="<?= lang('Config.gcaptcha_tooltip') ?>"></span>
|
||||
</a>
|
||||
</label>
|
||||
@@ -408,18 +405,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<?= form_label(lang('Config.customer_display'), 'customer_display_enabled', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-1">
|
||||
<?= form_checkbox([
|
||||
'name' => 'customer_display_enabled',
|
||||
'id' => 'customer_display_enabled',
|
||||
'value' => 'customer_display_enabled',
|
||||
'checked' => ($config['customer_display_enabled'] ?? 1) == 1
|
||||
]) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<?= form_label(lang('Config.show_office_group'), 'show_office_group', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-1">
|
||||
@@ -456,7 +441,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<?= form_label(lang('Config.category_dropdown'), 'category_dropdown', ['class' => 'control-label col-xs-2']) ?>
|
||||
<div class="col-xs-1">
|
||||
@@ -483,8 +467,8 @@
|
||||
<script type="text/javascript">
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
var enable_disable_gcaptcha_enable = (function() {
|
||||
var gcaptcha_enable = $("#gcaptcha_enable").is(":checked");
|
||||
const enable_disable_gcaptcha_enable = (function() {
|
||||
const gcaptcha_enable = $("#gcaptcha_enable").is(":checked");
|
||||
if (gcaptcha_enable) {
|
||||
$("#gcaptcha_site_key, #gcaptcha_secret_key").prop("disabled", !gcaptcha_enable).addClass("required");
|
||||
$("#config_gcaptcha_site_key, #config_gcaptcha_secret_key").addClass("required");
|
||||
@@ -557,6 +541,3 @@
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,6 @@
|
||||
<span class="glyphicon glyphicon-phone-alt"></span>
|
||||
</span>
|
||||
<?= form_input([
|
||||
'type' => 'tel',
|
||||
'name' => 'phone',
|
||||
'id' => 'phone',
|
||||
'class' => 'form-control input-sm required',
|
||||
@@ -123,7 +122,6 @@
|
||||
<span class="glyphicon glyphicon-phone-alt"></span>
|
||||
</span>
|
||||
<?= form_input([
|
||||
'type' => 'tel',
|
||||
'name' => 'fax',
|
||||
'id' => 'fax',
|
||||
'class' => 'form-control input-sm',
|
||||
|
||||
@@ -198,9 +198,9 @@
|
||||
<script type="text/javascript">
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
var enable_disable_invoice_enable = (function() {
|
||||
var invoice_enabled = $("#invoice_enable").is(":checked");
|
||||
var work_order_enabled = $("#work_order_enable").is(":checked");
|
||||
const enable_disable_invoice_enable = (function() {
|
||||
const invoice_enabled = $("#invoice_enable").is(":checked");
|
||||
const work_order_enabled = $("#work_order_enable").is(":checked");
|
||||
$("#sales_invoice_format, #recv_invoice_format, #invoice_default_comments, #invoice_email_message, select[name='invoice_type'], #sales_quote_format, select[name='line_sequence'], #last_used_invoice_number, #last_used_quote_number, #quote_default_comments, #work_order_enable, #work_order_format, #last_used_work_order_number").prop("disabled", !invoice_enabled);
|
||||
if (invoice_enabled) {
|
||||
$("#work_order_format, #last_used_work_order_number").prop("disabled", !work_order_enabled);
|
||||
@@ -210,9 +210,9 @@
|
||||
return arguments.callee;
|
||||
})();
|
||||
|
||||
var enable_disable_work_order_enable = (function() {
|
||||
var work_order_enabled = $("#work_order_enable").is(":checked");
|
||||
var invoice_enabled = $("#invoice_enable").is(":checked");
|
||||
const enable_disable_work_order_enable = (function() {
|
||||
const work_order_enabled = $("#work_order_enable").is(":checked");
|
||||
const invoice_enabled = $("#invoice_enable").is(":checked");
|
||||
if (invoice_enabled) {
|
||||
$("#work_order_format, #last_used_work_order_number").prop("disabled", !work_order_enabled);
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
$('span').tooltip();
|
||||
|
||||
$('#currency_symbol, #thousands_separator, #currency_code').change(function() {
|
||||
var data = {
|
||||
const data = {
|
||||
number_locale: $('#number_locale').val()
|
||||
};
|
||||
data['save_number_locale'] = $("input[name='save_number_locale']").val();
|
||||
@@ -336,7 +336,7 @@
|
||||
}
|
||||
},
|
||||
dataFilter: function(data) {
|
||||
var response = JSON.parse(data);
|
||||
const response = JSON.parse(data);
|
||||
$("input[name='save_number_locale']").val(response.save_number_locale);
|
||||
$('#number_locale_example').text(response.number_locale_example);
|
||||
$('#currency_symbol').val(response.currency_symbol);
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
<li role="presentation">
|
||||
<a data-toggle="tab" href="#invoice_tab" title="<?= lang('Config.invoice_configuration') ?>"><?= lang('Config.invoice') ?></a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a data-toggle="tab" href="#shortcuts_tab" title="<?= lang('Config.shortcuts_configuration') ?>"><?= lang('Config.shortcuts') ?></a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a data-toggle="tab" href="#reward_tab" title="<?= lang('Config.reward_configuration') ?>"><?= lang('Config.reward') ?></a>
|
||||
</li>
|
||||
@@ -68,9 +65,6 @@
|
||||
<div class="tab-pane" id="invoice_tab">
|
||||
<?= view('configs/invoice_config') ?>
|
||||
</div>
|
||||
<div class="tab-pane" id="shortcuts_tab">
|
||||
<?= view('configs/shortcuts_config') ?>
|
||||
</div>
|
||||
<div class="tab-pane" id="reward_tab">
|
||||
<?= view('configs/reward_config') ?>
|
||||
</div>
|
||||
|
||||
@@ -338,9 +338,9 @@
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
if (window.localStorage && window.jsPrintSetup) {
|
||||
var printers = (jsPrintSetup.getPrintersList() && jsPrintSetup.getPrintersList().split(',')) || [];
|
||||
const printers = (jsPrintSetup.getPrintersList() && jsPrintSetup.getPrintersList().split(',')) || [];
|
||||
$('#receipt_printer, #invoice_printer, #takings_printer').each(function() {
|
||||
var $this = $(this)
|
||||
const $this = $(this)
|
||||
$(printers).each(function(key, value) {
|
||||
$this.append($('<option>', {
|
||||
value: value
|
||||
@@ -360,7 +360,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
var dialog_confirmed = window.jsPrintSetup;
|
||||
const dialog_confirmed = window.jsPrintSetup;
|
||||
|
||||
$('#receipt_config_form').validate($.extend(form_support.handler, {
|
||||
submitHandler: function(form) {
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
|
||||
var enable_disable_customer_reward_enable = (function() {
|
||||
var customer_reward_enable = $("#customer_reward_enable").is(":checked");
|
||||
const enable_disable_customer_reward_enable = (function() {
|
||||
const customer_reward_enable = $("#customer_reward_enable").is(":checked");
|
||||
$("input[name*='customer_reward']:not(input[name=customer_reward_enable])").prop("disabled", !customer_reward_enable);
|
||||
$("input[name*='reward_points_']:not(input[name=customer_reward_enable])").prop("disabled", !customer_reward_enable);
|
||||
if (customer_reward_enable) {
|
||||
@@ -57,9 +57,9 @@
|
||||
|
||||
$("#customer_reward_enable").change(enable_disable_customer_reward_enable);
|
||||
|
||||
var table_count = <?= sizeof($customer_rewards) ?>;
|
||||
let table_count = <?= sizeof($customer_rewards) ?>;
|
||||
|
||||
var hide_show_remove = function() {
|
||||
const hide_show_remove = function() {
|
||||
if ($("input[name*='customer_rewards']:enabled").length > 1) {
|
||||
$(".remove_customer_rewards").show();
|
||||
} else {
|
||||
@@ -67,27 +67,27 @@
|
||||
}
|
||||
};
|
||||
|
||||
var add_customer_reward = function() {
|
||||
var id = $(this).parent().find('input').attr('id');
|
||||
const add_customer_reward = function() {
|
||||
let id = $(this).parent().find('input').attr('id');
|
||||
id = id.replace(/.*?_(\d+)$/g, "$1");
|
||||
var previous_id = 'customer_reward_' + id;
|
||||
var previous_id_next = 'reward_points_' + id;
|
||||
var block = $(this).parent().clone(true);
|
||||
var new_block = block.insertAfter($(this).parent());
|
||||
var new_block_id = 'customer_reward_' + ++id;
|
||||
var new_block_id_next = 'reward_points_' + id;
|
||||
const previous_id = 'customer_reward_' + id;
|
||||
const previous_id_next = 'reward_points_' + id;
|
||||
const block = $(this).parent().clone(true);
|
||||
const new_block = block.insertAfter($(this).parent());
|
||||
const new_block_id = 'customer_reward_' + ++id;
|
||||
const new_block_id_next = 'reward_points_' + id;
|
||||
$(new_block).find('label').html("<?= lang('Config.customer_reward') ?> " + ++table_count).attr('for', new_block_id).attr('class', 'control-label col-xs-2');
|
||||
$(new_block).find("input[id='" + previous_id + "']").attr('id', new_block_id).removeAttr('disabled').attr('name', new_block_id).attr('class', 'form-control input-sm').val('');
|
||||
$(new_block).find("input[id='" + previous_id_next + "']").attr('id', new_block_id_next).removeAttr('disabled').attr('name', new_block_id_next).attr('class', 'form-control input-sm').val('');
|
||||
hide_show_remove();
|
||||
};
|
||||
|
||||
var remove_customer_reward = function() {
|
||||
const remove_customer_reward = function() {
|
||||
$(this).parent().remove();
|
||||
hide_show_remove();
|
||||
};
|
||||
|
||||
var init_add_remove_tables = function() {
|
||||
const init_add_remove_tables = function() {
|
||||
$('.add_customer_reward').click(add_customer_reward);
|
||||
$('.remove_customer_reward').click(remove_customer_reward);
|
||||
hide_show_remove();
|
||||
@@ -96,10 +96,10 @@
|
||||
};
|
||||
init_add_remove_tables();
|
||||
|
||||
var duplicate_found = false;
|
||||
const duplicate_found = false;
|
||||
// Run validator once for all fields
|
||||
$.validator.addMethod('customer_reward', function(value, element) {
|
||||
var value_count = 0;
|
||||
let value_count = 0;
|
||||
$("input[name*='customer_reward']:not(input[name=customer_reward_enable])").each(function() {
|
||||
value_count = $(this).val() == value ? value_count + 1 : value_count;
|
||||
});
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @var array $config
|
||||
* @var array $keyboardShortcutOptions
|
||||
* @var array $keyboardShortcuts
|
||||
*/
|
||||
|
||||
$keyboardShortcuts ??= [];
|
||||
$keyboardShortcutOptions ??= [];
|
||||
$config ??= [];
|
||||
|
||||
$shortcutLabels = [
|
||||
'cancel' => lang('Sales.key_cancel'),
|
||||
'items' => lang('Sales.key_item_search'),
|
||||
'customers' => lang('Sales.key_customer_search'),
|
||||
'suspend' => lang('Sales.key_suspend'),
|
||||
'suspended' => lang('Sales.key_suspended'),
|
||||
'amount' => lang('Sales.key_tendered'),
|
||||
'payment' => lang('Sales.key_payment'),
|
||||
'complete' => lang('Sales.key_finish_sale'),
|
||||
'finish' => lang('Sales.key_finish_quote'),
|
||||
'help' => lang('Sales.key_help_modal')
|
||||
];
|
||||
?>
|
||||
|
||||
<?= form_open('config/saveShortcuts', ['id' => 'shortcuts_config_form', 'class' => 'form-horizontal']) ?>
|
||||
<div id="config_wrapper">
|
||||
<div class="row">
|
||||
<fieldset id="config_info">
|
||||
<div class="col-md-8">
|
||||
<div id="required_fields_message"><?= esc(lang('Common.fields_required_message')) ?></div>
|
||||
<ul id="shortcuts_error_message_box" class="error_message_box"></ul>
|
||||
|
||||
<?php foreach ($shortcutLabels as $name => $label): ?>
|
||||
<div class="form-group form-group-sm">
|
||||
<?= form_label($label, 'key_' . $name, ['class' => 'control-label col-xs-3']) ?>
|
||||
<div class="col-xs-4">
|
||||
<?php $keyboardShortcutSelectedValue = $keyboardShortcuts[$name]['value'] ?? ''; ?>
|
||||
<?= form_dropdown(
|
||||
'key_' . $name,
|
||||
$keyboardShortcutOptions,
|
||||
$keyboardShortcutSelectedValue,
|
||||
'class="form-control input-sm"'
|
||||
) ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<div class="col-xs-12 clearfix">
|
||||
<?= form_submit([
|
||||
'name' => 'submit_shortcuts',
|
||||
'id' => 'submit_shortcuts',
|
||||
'value' => lang('Common.submit'),
|
||||
'class' => 'btn btn-primary btn-sm pull-right'
|
||||
]) ?>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<?= form_close() ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#shortcuts_config_form').validate($.extend(form_support.handler, {
|
||||
submitHandler: function(form) {
|
||||
$(form).ajaxSubmit({
|
||||
success: function(response) {
|
||||
$.notify({
|
||||
message: response.message
|
||||
}, {
|
||||
type: response.success ? 'success' : 'danger'
|
||||
});
|
||||
},
|
||||
error: function(xhr) {
|
||||
const rawMessage = xhr.responseJSON?.message ?? xhr.responseText ?? <?= json_encode(lang('Config.shortcuts_save_error')) ?>;
|
||||
$.notify({
|
||||
message: DOMPurify.sanitize(rawMessage)
|
||||
}, {
|
||||
type: 'danger'
|
||||
});
|
||||
},
|
||||
dataType: 'json'
|
||||
});
|
||||
},
|
||||
|
||||
errorLabelContainer: '#shortcuts_error_message_box'
|
||||
}));
|
||||
</script>
|
||||
@@ -29,9 +29,9 @@
|
||||
<script type="text/javascript">
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
var location_count = <?= sizeof($stock_locations) ?>;
|
||||
let location_count = <?= sizeof($stock_locations) ?>;
|
||||
|
||||
var hide_show_remove = function() {
|
||||
const hide_show_remove = function() {
|
||||
if ($("input[name*='stock_location']:enabled").length > 1) {
|
||||
$(".remove_stock_location").show();
|
||||
} else {
|
||||
@@ -39,31 +39,31 @@
|
||||
}
|
||||
};
|
||||
|
||||
var add_stock_location = function() {
|
||||
var block = $(this).parent().clone(true);
|
||||
var new_block = block.insertAfter($(this).parent());
|
||||
var new_block_id = 'stock_location[]';
|
||||
const add_stock_location = function() {
|
||||
const block = $(this).parent().clone(true);
|
||||
const new_block = block.insertAfter($(this).parent());
|
||||
const new_block_id = 'stock_location[]';
|
||||
$(new_block).find('label').html("<?= lang('Config.stock_location') ?> " + ++location_count).attr('for', new_block_id).attr('class', 'control-label col-xs-2');
|
||||
$(new_block).find('input').attr('id', new_block_id).removeAttr('disabled').attr('name', new_block_id).attr('class', 'form-control input-sm').val('');
|
||||
hide_show_remove();
|
||||
};
|
||||
|
||||
var remove_stock_location = function() {
|
||||
const remove_stock_location = function() {
|
||||
$(this).parent().remove();
|
||||
hide_show_remove();
|
||||
};
|
||||
|
||||
var init_add_remove_locations = function() {
|
||||
const init_add_remove_locations = function() {
|
||||
$('.add_stock_location').click(add_stock_location);
|
||||
$('.remove_stock_location').click(remove_stock_location);
|
||||
hide_show_remove();
|
||||
};
|
||||
init_add_remove_locations();
|
||||
|
||||
var duplicate_found = false;
|
||||
const duplicate_found = false;
|
||||
// Run validator once for all fields
|
||||
$.validator.addMethod('stock_location', function(value, element) {
|
||||
var value_count = 0;
|
||||
let value_count = 0;
|
||||
$("input[name*='stock_location']").each(function() {
|
||||
value_count = $(this).val() == value ? value_count + 1 : value_count;
|
||||
});
|
||||
|
||||
@@ -25,8 +25,8 @@ use Config\OSPOS;
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-2" style="text-align: left;"><br>
|
||||
<p style="min-height: 17.7em; font-weight: bold;">General Info</p>
|
||||
<p style="min-height: 12.2em; font-weight: bold;">User Setup</p><br>
|
||||
<p style="min-height: 14.7em; font-weight: bold;">General Info</p>
|
||||
<p style="min-height: 10.5em; font-weight: bold;">User Setup</p><br>
|
||||
<p style="font-weight: bold;">Permissions</p>
|
||||
</div>
|
||||
<div class="col-sm-8" id="issuetemplate" style="text-align: left;"><br>
|
||||
@@ -42,7 +42,7 @@ use Config\OSPOS;
|
||||
echo "» OpenSSL: ", extension_loaded('openssl') ? '<span style="color: green;">Enabled ✓</span>' : '<span style="color: red;">Disabled ✗</span>', '<br>';
|
||||
echo "» MBString: ", extension_loaded('mbstring') ? '<span style="color: green;">Enabled ✓</span>' : '<span style="color: red;">Disabled ✗</span>', '<br>';
|
||||
echo "» Curl: ", extension_loaded('curl') ? '<span style="color: green;">Enabled ✓</span>' : '<span style="color: red;">Disabled ✗</span>', '<br>';
|
||||
echo "» Json: ", extension_loaded('json') ? '<span style="color: green;">Enabled ✓</span>' : '<span style="color: red;">Disabled ✗</span>', '<br>';
|
||||
echo "» Json: ", extension_loaded('json') ? '<span style="color: green;">Enabled ✓</span>' : '<span style="color: red;">Disabled ✗</span>', '<br><br>';
|
||||
echo "» Xml: ", extension_loaded('xml') ? '<span style="color: green;">Enabled ✓</span>' : '<span style="color: red;">Disabled ✗</span>', '<br><br>';
|
||||
?>
|
||||
User Configuration:<br>
|
||||
@@ -198,7 +198,7 @@ use Config\OSPOS;
|
||||
<div style="text-align: center;">
|
||||
<a class="copy" data-clipboard-action="copy" data-clipboard-target="#issuetemplate">Copy Info</a> | <a href="https://github.com/opensourcepos/opensourcepos/issues/new" target="_blank"> <?= lang('Config.report_an_issue') ?></a>
|
||||
<script type="text/javascript">
|
||||
var clipboard = new ClipboardJS('.copy');
|
||||
const clipboard = new ClipboardJS('.copy');
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
document.getSelection().removeAllRanges();
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
|
||||
var enable_disable_dinner_table_enable = (function() {
|
||||
var dinner_table_enable = $("#dinner_table_enable").is(":checked");
|
||||
const enable_disable_dinner_table_enable = (function() {
|
||||
const dinner_table_enable = $("#dinner_table_enable").is(":checked");
|
||||
$("input[name*='dinner_table']:not(input[name=dinner_table_enable])").prop("disabled", !dinner_table_enable);
|
||||
if (dinner_table_enable) {
|
||||
$(".add_dinner_table, .remove_dinner_table").show();
|
||||
@@ -56,9 +56,9 @@
|
||||
|
||||
$("#dinner_table_enable").change(enable_disable_dinner_table_enable);
|
||||
|
||||
var table_count = <?= sizeof($dinner_tables) ?>;
|
||||
let table_count = <?= sizeof($dinner_tables) ?>;
|
||||
|
||||
var hide_show_remove = function() {
|
||||
const hide_show_remove = function() {
|
||||
if ($("input[name*='dinner_tables']:enabled").length > 1) {
|
||||
$(".remove_dinner_tables").show();
|
||||
} else {
|
||||
@@ -66,23 +66,23 @@
|
||||
}
|
||||
};
|
||||
|
||||
var add_dinner_table = function() {
|
||||
var id = $(this).parent().find('input').attr('id');
|
||||
const add_dinner_table = function() {
|
||||
let id = $(this).parent().find('input').attr('id');
|
||||
id = id.replace(/.*?_(\d+)$/g, "$1");
|
||||
var block = $(this).parent().clone(true);
|
||||
var new_block = block.insertAfter($(this).parent());
|
||||
var new_block_id = 'dinner_table_' + ++id;
|
||||
const block = $(this).parent().clone(true);
|
||||
const new_block = block.insertAfter($(this).parent());
|
||||
const new_block_id = 'dinner_table_' + ++id;
|
||||
$(new_block).find('label').html("<?= lang('Config.dinner_table') ?> " + ++table_count).attr('for', new_block_id).attr('class', 'control-label col-xs-2');
|
||||
$(new_block).find('input').attr('id', new_block_id).removeAttr('disabled').attr('name', new_block_id).attr('class', 'form-control input-sm').val('');
|
||||
hide_show_remove();
|
||||
};
|
||||
|
||||
var remove_dinner_table = function() {
|
||||
const remove_dinner_table = function() {
|
||||
$(this).parent().remove();
|
||||
hide_show_remove();
|
||||
};
|
||||
|
||||
var init_add_remove_tables = function() {
|
||||
const init_add_remove_tables = function() {
|
||||
$('.add_dinner_table').click(add_dinner_table);
|
||||
$('.remove_dinner_table').click(remove_dinner_table);
|
||||
hide_show_remove();
|
||||
@@ -91,10 +91,10 @@
|
||||
};
|
||||
init_add_remove_tables();
|
||||
|
||||
var duplicate_found = false;
|
||||
const duplicate_found = false;
|
||||
// Run validator once for all fields
|
||||
$.validator.addMethod('dinner_table', function(value, element) {
|
||||
var value_count = 0;
|
||||
let value_count = 0;
|
||||
$("input[name*='dinner_table']:not(input[name=dinner_table_enable])").each(function() {
|
||||
value_count = $(this).val() == value ? value_count + 1 : value_count;
|
||||
});
|
||||
|
||||
@@ -51,10 +51,6 @@
|
||||
</div>
|
||||
<div class="col-xs-1 input-group">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'step' => 'any',
|
||||
'min' => '0',
|
||||
'max' => '100',
|
||||
'name' => 'default_tax_1_rate',
|
||||
'id' => 'default_tax_1_rate',
|
||||
'class' => 'form-control input-sm',
|
||||
@@ -76,10 +72,6 @@
|
||||
</div>
|
||||
<div class="col-xs-1 input-group">
|
||||
<?= form_input([
|
||||
'type' => 'number',
|
||||
'step' => 'any',
|
||||
'min' => '0',
|
||||
'max' => '100',
|
||||
'name' => 'default_tax_2_rate',
|
||||
'id' => 'default_tax_2_rate',
|
||||
'class' => 'form-control input-sm',
|
||||
@@ -151,8 +143,8 @@
|
||||
<script type="text/javascript">
|
||||
// Validation and submit handling
|
||||
$(document).ready(function() {
|
||||
var enable_disable_use_destination_based_tax = (function() {
|
||||
var use_destination_based_tax = $("#use_destination_based_tax").is(":checked");
|
||||
const enable_disable_use_destination_based_tax = (function() {
|
||||
const use_destination_based_tax = $("#use_destination_based_tax").is(":checked");
|
||||
$("select[name='default_tax_code']").prop("disabled", !use_destination_based_tax);
|
||||
$("select[name='default_tax_category']").prop("disabled", !use_destination_based_tax);
|
||||
$("select[name='default_tax_jurisdiction']").prop("disabled", !use_destination_based_tax);
|
||||
|
||||
@@ -453,7 +453,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
var fill_value = function(event, ui) {
|
||||
const fill_value = function(event, ui) {
|
||||
event.preventDefault();
|
||||
$("input[name='sales_tax_code_id']").val(ui.item.value);
|
||||
$("input[name='sales_tax_code_name']").val(ui.item.label);
|
||||
|
||||
@@ -167,10 +167,10 @@
|
||||
});
|
||||
|
||||
$.validator.addMethod('module', function(value, element) {
|
||||
var result = $('#permission_list input').is(':checked');
|
||||
let result = $('#permission_list input').is(':checked');
|
||||
$('.module').each(function(index, element) {
|
||||
var parent = $(element).parent();
|
||||
var checked = $(element).is(':checked');
|
||||
const parent = $(element);
|
||||
const checked = $(element).is(':checked');
|
||||
if ($('ul', parent).length > 0 && result) {
|
||||
result &= !checked || (checked && $('ul > li > input:checked', parent).length > 0);
|
||||
}
|
||||
@@ -179,10 +179,10 @@
|
||||
}, "<?= lang('Employees.subpermission_required') ?>");
|
||||
|
||||
$('ul#permission_list > li > input.module').each(function() {
|
||||
var $this = $(this);
|
||||
const $this = $(this);
|
||||
$('ul > li > input,select', $this.parent()).each(function() {
|
||||
var $that = $(this);
|
||||
var updateInputs = function(checked) {
|
||||
const $that = $(this);
|
||||
const updateInputs = function(checked) {
|
||||
$that.prop('disabled', !checked);
|
||||
!checked && $that.prop('checked', false);
|
||||
}
|
||||
|
||||
@@ -101,11 +101,9 @@ p.lead {
|
||||
}
|
||||
|
||||
.tabs {
|
||||
list-style: none;
|
||||
list-style-position: inside;
|
||||
margin: 0;
|
||||
list-style: none inside none;
|
||||
padding: 0;
|
||||
margin-bottom: -1px;
|
||||
margin: 0 0 -1px;
|
||||
}
|
||||
.tabs li {
|
||||
display: inline;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
var tabLinks = new Array();
|
||||
var contentDivs = new Array();
|
||||
const tabLinks = new Array();
|
||||
const contentDivs = new Array();
|
||||
|
||||
function init()
|
||||
{
|
||||
// Grab the tab links and content divs from the page
|
||||
var tabListItems = document.getElementById('tabs').childNodes;
|
||||
const tabListItems = document.getElementById('tabs').childNodes;
|
||||
console.log(tabListItems);
|
||||
for (var i = 0; i < tabListItems.length; i ++)
|
||||
for (let i = 0; i < tabListItems.length; i ++)
|
||||
{
|
||||
if (tabListItems[i].nodeName == "LI")
|
||||
{
|
||||
var tabLink = getFirstChildWithTagName(tabListItems[i], 'A');
|
||||
var id = getHash(tabLink.getAttribute('href'));
|
||||
const tabLink = getFirstChildWithTagName(tabListItems[i], 'A');
|
||||
const id = getHash(tabLink.getAttribute('href'));
|
||||
tabLinks[id] = tabLink;
|
||||
contentDivs[id] = document.getElementById(id);
|
||||
}
|
||||
@@ -19,9 +19,9 @@ function init()
|
||||
|
||||
// Assign onclick events to the tab links, and
|
||||
// highlight the first tab
|
||||
var i = 0;
|
||||
let i = 0;
|
||||
|
||||
for (var id in tabLinks)
|
||||
for (const id in tabLinks)
|
||||
{
|
||||
tabLinks[id].onclick = showTab;
|
||||
tabLinks[id].onfocus = function () {
|
||||
@@ -35,26 +35,26 @@ function init()
|
||||
}
|
||||
|
||||
// Hide all content divs except the first
|
||||
var i = 0;
|
||||
let j = 0;
|
||||
|
||||
for (var id in contentDivs)
|
||||
for (const id in contentDivs)
|
||||
{
|
||||
if (i != 0)
|
||||
if (j != 0)
|
||||
{
|
||||
console.log(contentDivs[id]);
|
||||
contentDivs[id].className = 'content hide';
|
||||
}
|
||||
i ++;
|
||||
j ++;
|
||||
}
|
||||
}
|
||||
|
||||
function showTab()
|
||||
{
|
||||
var selectedId = getHash(this.getAttribute('href'));
|
||||
const selectedId = getHash(this.getAttribute('href'));
|
||||
|
||||
// Highlight the selected tab, and dim all others.
|
||||
// Also show the selected content div, and hide all others.
|
||||
for (var id in contentDivs)
|
||||
for (const id in contentDivs)
|
||||
{
|
||||
if (id == selectedId)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ function showTab()
|
||||
|
||||
function getFirstChildWithTagName(element, tagName)
|
||||
{
|
||||
for (var i = 0; i < element.childNodes.length; i ++)
|
||||
for (let i = 0; i < element.childNodes.length; i ++)
|
||||
{
|
||||
if (element.childNodes[i].nodeName == tagName)
|
||||
{
|
||||
@@ -85,28 +85,29 @@ function getFirstChildWithTagName(element, tagName)
|
||||
|
||||
function getHash(url)
|
||||
{
|
||||
var hashPos = url.lastIndexOf('#');
|
||||
const hashPos = url.lastIndexOf('#');
|
||||
return url.substring(hashPos + 1);
|
||||
}
|
||||
|
||||
function toggle(elem)
|
||||
{
|
||||
elem = document.getElementById(elem);
|
||||
let disp;
|
||||
|
||||
if (elem.style && elem.style['display'])
|
||||
{
|
||||
// Only works with the "style" attr
|
||||
var disp = elem.style['display'];
|
||||
disp = elem.style['display'];
|
||||
}
|
||||
else if (elem.currentStyle)
|
||||
{
|
||||
// For MSIE, naturally
|
||||
var disp = elem.currentStyle['display'];
|
||||
disp = elem.currentStyle['display'];
|
||||
}
|
||||
else if (window.getComputedStyle)
|
||||
{
|
||||
// For most other browsers
|
||||
var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display');
|
||||
disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display');
|
||||
}
|
||||
|
||||
// Toggle the state of the "display" style
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
!$(this).val() && $(this).val('');
|
||||
});
|
||||
|
||||
var fill_value = function(event, ui) {
|
||||
const fill_value = function(event, ui) {
|
||||
event.preventDefault();
|
||||
$(this).val((ui.item ? ui.item.label : ""));
|
||||
$("input[name='person_id']").val(ui.item.value);
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
var fill_value = function(event, ui) {
|
||||
const fill_value = function(event, ui) {
|
||||
event.preventDefault();
|
||||
$("input[name='kit_item_id']").val(ui.item.value);
|
||||
$("input[name='item_name']").val(DOMPurify.sanitize(ui.item.label));
|
||||
|
||||
@@ -467,7 +467,7 @@
|
||||
!$(this).val() && $(this).val('');
|
||||
});
|
||||
|
||||
var fill_tax_category_value = function(event, ui) {
|
||||
const fill_tax_category_value = function(event, ui) {
|
||||
event.preventDefault();
|
||||
$("input[name='tax_category_id']").val(ui.item.value);
|
||||
$("input[name='tax_category']").val(ui.item.label);
|
||||
@@ -483,7 +483,7 @@
|
||||
focus: fill_tax_category_value
|
||||
});
|
||||
|
||||
var fill_low_sell_value = function(event, ui) {
|
||||
const fill_low_sell_value = function(event, ui) {
|
||||
event.preventDefault();
|
||||
$("input[name='low_sell_item_id']").val(ui.item.value);
|
||||
$("input[name='low_sell_item_name']").val(ui.item.label);
|
||||
@@ -517,7 +517,7 @@
|
||||
return value.match(/(\||_)/g) == null;
|
||||
}, "<?= lang('Attributes.attribute_value_invalid_chars') ?>");
|
||||
|
||||
var init_validation = function() {
|
||||
const init_validation = function() {
|
||||
$('#item_form').validate($.extend({
|
||||
submitHandler: function(form, event) { // Event is not used as a parameter here
|
||||
$(form).ajaxSubmit({
|
||||
|
||||
@@ -178,10 +178,10 @@
|
||||
delay: 10
|
||||
});
|
||||
|
||||
var confirm_message = false;
|
||||
let confirm_message = false;
|
||||
$('#tax_percent_name_2, #tax_name_2').prop('disabled', true),
|
||||
$('#tax_percent_name_1, #tax_name_1').blur(function() {
|
||||
var disabled = !($('#tax_percent_name_1').val() + $('#tax_name_1').val());
|
||||
const disabled = !($('#tax_percent_name_1').val() + $('#tax_name_1').val());
|
||||
$('#tax_percent_name_2, #tax_name_2').prop('disabled', disabled);
|
||||
confirm_message = disabled ? '' : "<?= lang('Items.confirm_bulk_edit_wipe_taxes') ?>";
|
||||
});
|
||||
|
||||
@@ -115,27 +115,27 @@ use App\Models\Inventory;
|
||||
});
|
||||
|
||||
function display_stock(location_id) {
|
||||
var item_quantities = <?= json_encode(esc($item_quantities, 'raw')) ?>;
|
||||
const item_quantities = <?= json_encode(esc($item_quantities, 'raw')) ?>;
|
||||
document.getElementById("quantity").value = parseFloat(item_quantities[location_id]).toFixed(<?= quantity_decimals() ?>);
|
||||
|
||||
var inventory_data = <?= json_encode(esc($inventory_array, 'raw')) ?>;
|
||||
var employee_data = <?= json_encode(esc($employee_name, 'raw')) ?>;
|
||||
const inventory_data = <?= json_encode(esc($inventory_array, 'raw')) ?>;
|
||||
const employee_data = <?= json_encode(esc($employee_name, 'raw')) ?>;
|
||||
|
||||
var table = document.getElementById("inventory_result");
|
||||
const table = document.getElementById("inventory_result");
|
||||
|
||||
// Remove old query from tbody
|
||||
var rowCount = table.rows.length;
|
||||
for (var index = rowCount; index > 0; index--) {
|
||||
const rowCount = table.rows.length;
|
||||
for (let index = rowCount; index > 0; index--) {
|
||||
table.deleteRow(index - 1);
|
||||
}
|
||||
|
||||
// Add new query to tbody
|
||||
for (var index = 0; index < inventory_data.length; index++) {
|
||||
var data = inventory_data[index];
|
||||
for (let index = 0; index < inventory_data.length; index++) {
|
||||
const data = inventory_data[index];
|
||||
if (data['trans_location'] == location_id) {
|
||||
var tr = document.createElement('tr');
|
||||
const tr = document.createElement('tr');
|
||||
|
||||
var td = document.createElement('td');
|
||||
let td = document.createElement('td');
|
||||
td.appendChild(document.createTextNode(data['trans_date']));
|
||||
tr.appendChild(td);
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
});
|
||||
|
||||
function fill_quantity(val) {
|
||||
var item_quantities = <?= json_encode(esc($item_quantities, 'raw')) ?>;
|
||||
const item_quantities = <?= json_encode(esc($item_quantities, 'raw')) ?>;
|
||||
document.getElementById('quantity').value = parseFloat(item_quantities[val]).toFixed(<?= quantity_decimals() ?>);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -30,7 +30,7 @@ use App\Models\Employee;
|
||||
// Set the beginning of time as starting date
|
||||
$('#daterangepicker').data('daterangepicker').setStartDate("<?= date($config['dateformat'], mktime(0, 0, 0, 01, 01, 2010)) ?>");
|
||||
// Update the hidden inputs with the selected dates before submitting the search data
|
||||
var start_date = "<?= date('Y-m-d', mktime(0, 0, 0, 01, 01, 2010)) ?>";
|
||||
start_date = "<?= date('Y-m-d', mktime(0, 0, 0, 01, 01, 2010)) ?>";
|
||||
|
||||
// Override dates from server if provided
|
||||
<?php if (isset($start_date) && $start_date): ?>
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
* @var bool $is_new_install
|
||||
* @var string $latest_version
|
||||
* @var bool $gcaptcha_enabled
|
||||
* @var CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @var array $config
|
||||
* @var $validation
|
||||
*/
|
||||
|
||||
use Config\Services;
|
||||
|
||||
$request = Services::request();
|
||||
?>
|
||||
|
||||
<!doctype html>
|
||||
@@ -159,6 +154,11 @@ $request = Services::request();
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<?php
|
||||
use Config\Services;
|
||||
$request = Services::request();
|
||||
?>
|
||||
|
||||
<?php if (ENVIRONMENT == 'development' || get_cookie('debug') == 'true' || $request->getGet('debug') == 'true') : ?>
|
||||
<!-- inject:login:debug:js -->
|
||||
<!-- endinject -->
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @var array $config
|
||||
* @var string $companyName
|
||||
* @var string $companyDetails
|
||||
*/
|
||||
|
||||
helper('url');
|
||||
?>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="<?= esc(service('request')->getLocale()) ?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= lang('Sales.customer_display') ?></title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="<?= base_url('images/favicon.ico') ?>">
|
||||
<link rel="stylesheet" href="<?= base_url('resources/bootswatch/' . (empty($config['theme']) ? 'flatly' : esc($config['theme'])) . '/bootstrap.min.css') ?>">
|
||||
<link rel="stylesheet" href="<?= base_url('resources/opensourcepos-8e34d6a398.min.css') ?>">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f8f8f8;
|
||||
color: #333;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.customer-display-header {
|
||||
background: #1f3143;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
padding: 6px 12px;
|
||||
border-bottom: 1px solid #102131;
|
||||
}
|
||||
|
||||
.customer-display-shell {
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 12px 18px 18px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.customer-display-company {
|
||||
text-align: center;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.customer-display-company img {
|
||||
display: block;
|
||||
margin: 0 auto 6px;
|
||||
max-height: 84px;
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
.customer-display-company .company-name {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.customer-display-company .company-details {
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.customer-display-company .company-phone {
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.customer-display-main-row {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: flex-start;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.customer-display-cart-column {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.customer-display-summary-column {
|
||||
flex: 0 0 320px;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel,
|
||||
.customer-display-info-panel,
|
||||
.customer-display-items-panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .panel-heading,
|
||||
.customer-display-info-panel .panel-heading,
|
||||
.customer-display-items-panel .panel-heading {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .table,
|
||||
.customer-display-info-table {
|
||||
margin-bottom: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .table > tbody > tr > th,
|
||||
.customer-display-info-table > tbody > tr > th {
|
||||
background: #f8fbfd;
|
||||
width: 56%;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .table > tbody > tr > td,
|
||||
.customer-display-info-table > tbody > tr > td {
|
||||
width: 44%;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .rate-row th,
|
||||
.customer-display-summary-panel .rate-row td {
|
||||
color: #c00000;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .summary-section-row th {
|
||||
background: #eaf2f8;
|
||||
color: #1f3b5b;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .summary-subtable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .summary-subtable > tbody > tr > th {
|
||||
background: #fdfefe;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .summary-subtable > tbody > tr > td {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.register-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#register {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
table-layout: fixed;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#register th,
|
||||
#register td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 6px 5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#register thead th {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#register tbody td {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#register tbody td.item-name-cell {
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#register tbody td.price-cell {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#register tbody td.serial-cell {
|
||||
font-size: 12px;
|
||||
color: #2F4F4F;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .table > tbody > tr > th,
|
||||
.customer-display-info-table > tbody > tr > th {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .table > tbody > tr > td,
|
||||
.customer-display-info-table > tbody > tr > td {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.customer-display-summary-panel .panel-body,
|
||||
.customer-display-info-panel .panel-body,
|
||||
.customer-display-items-panel .panel-body {
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
.customer-display-summary-column .panel-body {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.customer-display-summary-column .customer-name-value,
|
||||
.customer-display-summary-column .giftcard-value,
|
||||
.customer-display-summary-column .reward-value {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.customer-display-footer {
|
||||
margin-top: 14px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="customer-display-header">Open Source Point of Sale</div>
|
||||
<div class="customer-display-shell">
|
||||
<div class="customer-display-company">
|
||||
<?php if (!empty($config['company_logo'])) { ?>
|
||||
<img src="<?= base_url('uploads/' . esc($config['company_logo'], 'url')) ?>" alt="company_logo">
|
||||
<?php } ?>
|
||||
<div class="company-name"><?= esc($companyName) ?></div>
|
||||
<div class="company-phone">Phone: <?= esc((string)($config['phone'] ?? '')) ?></div>
|
||||
<?php if ($companyDetails !== '') { ?>
|
||||
<div class="company-details"><?= nl2br(esc($companyDetails)) ?></div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="customer-display-main-row">
|
||||
|
||||
@@ -3,7 +3,7 @@ use Config\OSPOS;
|
||||
|
||||
$config = config(OSPOS::class)->settings; ?>
|
||||
|
||||
var pickerconfig = function(config) {
|
||||
const pickerconfig = function(config) {
|
||||
return $.extend({
|
||||
format: "<?= $this->data["format"] ?? dateformat_bootstrap($config['dateformat']) . ' ' . dateformat_bootstrap($config['timeformat'])?>",
|
||||
<?php
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
<?php if (empty($config['date_or_time_format'])) { ?>
|
||||
$('#daterangepicker').css("width", "180");
|
||||
var start_date = "<?= date('Y-m-d') ?>";
|
||||
var end_date = "<?= date('Y-m-d') ?>";
|
||||
let start_date = "<?= date('Y-m-d') ?>";
|
||||
let end_date = "<?= date('Y-m-d') ?>";
|
||||
|
||||
$('#daterangepicker').daterangepicker({
|
||||
"ranges": {
|
||||
@@ -112,8 +112,8 @@
|
||||
});
|
||||
<?php } else { ?>
|
||||
$('#daterangepicker').css("width", "305");
|
||||
var start_date = "<?= date('Y-m-d H:i:s', mktime(0, 0, 0, date("m"), date("d"), date("Y"))) ?>";
|
||||
var end_date = "<?= date('Y-m-d H:i:s', mktime(23, 59, 59, date("m"), date("d"), date("Y"))) ?>";
|
||||
let start_date = "<?= date('Y-m-d H:i:s', mktime(0, 0, 0, date("m"), date("d"), date("Y"))) ?>";
|
||||
let end_date = "<?= date('Y-m-d H:i:s', mktime(23, 59, 59, date("m"), date("d"), date("Y"))) ?>";
|
||||
$('#daterangepicker').daterangepicker({
|
||||
"ranges": {
|
||||
"<?= lang('Datepicker.today') ?>": [
|
||||
|
||||
@@ -12,16 +12,14 @@ $request = Services::request();
|
||||
?>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="<?= current_language_code() ?>">
|
||||
<html lang="<?= $request->getLocale() ?>">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="<?= base_url() ?>">
|
||||
<title><?= esc($config['company']) . ' | ' . lang('Common.powered_by') . ' OSPOS ' . esc(config('App')->application_version) ?></title>
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico">
|
||||
<?php $theme = (empty($config['theme']) ? 'flatly' : esc($config['theme'])); ?>
|
||||
<link rel="stylesheet" href="resources/bootswatch/<?= "$theme" ?>/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="<?= 'resources/bootswatch/' . (empty($config['theme']) ? 'flatly' : esc($config['theme'])) . '/bootstrap.min.css' ?>">
|
||||
|
||||
<?php if (ENVIRONMENT == 'development' || get_cookie('debug') == 'true' || $request->getGet('debug') == 'true') : ?>
|
||||
<!-- inject:debug:css -->
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
// Live clock
|
||||
var clock_tick = function clock_tick() {
|
||||
const clock_tick = function clock_tick() {
|
||||
setInterval('update_clock();', 1000);
|
||||
}
|
||||
|
||||
// Start the clock immediately
|
||||
clock_tick();
|
||||
|
||||
var update_clock = function update_clock() {
|
||||
const update_clock = function update_clock() {
|
||||
document.getElementById('liveclock').innerHTML = moment().format("<?= dateformat_momentjs($config['dateformat'] . ' ' . $config['timeformat']) ?>");
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
var csrf_token = function() {
|
||||
return "<?= csrf_hash() ?>";
|
||||
const csrf_token = function() {
|
||||
return "<?= esc(csrf_hash(), 'js') ?>";
|
||||
};
|
||||
|
||||
var csrf_form_base = function() {
|
||||
const csrf_form_base = function() {
|
||||
return {
|
||||
<?= esc(config('Security')->tokenName, 'js') ?>: function() {
|
||||
return csrf_token()
|
||||
@@ -44,14 +44,14 @@
|
||||
}
|
||||
};
|
||||
|
||||
var setup_csrf_token = function() {
|
||||
const setup_csrf_token = function() {
|
||||
$('input[name="<?= esc(config('Security')->tokenName, 'js') ?>"]').val(csrf_token());
|
||||
};
|
||||
|
||||
var ajax = $.ajax;
|
||||
const ajax = $.ajax;
|
||||
|
||||
$.ajax = function() {
|
||||
var args = arguments[0];
|
||||
let args = arguments[0];
|
||||
if (args['type'] && args['type'].toLowerCase() == 'post' && csrf_token()) {
|
||||
if (typeof args['data'] === 'string') {
|
||||
args['data'] += '&' + $.param(csrf_form_base());
|
||||
@@ -80,7 +80,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
var submit = $.fn.submit;
|
||||
const submit = $.fn.submit;
|
||||
|
||||
$.fn.submit = function() {
|
||||
setup_csrf_token();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script type="text/javascript">
|
||||
(function(lang, $) {
|
||||
|
||||
var lines = {
|
||||
const lines = {
|
||||
'common_submit': "<?= lang('Common.submit') ?>",
|
||||
'common_close': "<?= lang('Common.close') ?>"
|
||||
};
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
jsPrintSetup.setOption('footerStrRight', '');
|
||||
<?php } ?>
|
||||
|
||||
var printers = jsPrintSetup.getPrintersList().split(',');
|
||||
const printers = jsPrintSetup.getPrintersList().split(',');
|
||||
// Get right printer here..
|
||||
for (var index in printers) {
|
||||
var default_ticket_printer = window.localStorage && localStorage['<?= esc($selected_printer, 'js') ?>'];
|
||||
var selected_printer = printers[index];
|
||||
for (const index in printers) {
|
||||
const default_ticket_printer = window.localStorage && localStorage['<?= esc($selected_printer, 'js') ?>'];
|
||||
const selected_printer = printers[index];
|
||||
if (selected_printer == default_ticket_printer) {
|
||||
// Select Epson label printer
|
||||
jsPrintSetup.setPrinter(selected_printer);
|
||||
|
||||
@@ -18,11 +18,11 @@ $filter_select_id = $options['filter_select_id'] ?? 'filters';
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var additional_params = <?= json_encode($additional_params) ?>;
|
||||
var filter_select_id = '<?= esc($filter_select_id) ?>';
|
||||
const additional_params = <?= json_encode($additional_params) ?>;
|
||||
const filter_select_id = '<?= esc($filter_select_id) ?>';
|
||||
|
||||
function update_url() {
|
||||
var params = new URLSearchParams();
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// Add dates
|
||||
if (typeof start_date !== 'undefined') {
|
||||
@@ -33,7 +33,7 @@ $filter_select_id = $options['filter_select_id'] ?? 'filters';
|
||||
}
|
||||
|
||||
// Add filters
|
||||
var filters = $('#' + filter_select_id).val();
|
||||
const filters = $('#' + filter_select_id).val();
|
||||
if (filters) {
|
||||
filters.forEach(function(filter) {
|
||||
params.append('filters[]', filter);
|
||||
@@ -42,9 +42,9 @@ $filter_select_id = $options['filter_select_id'] ?? 'filters';
|
||||
|
||||
// Add additional params
|
||||
additional_params.forEach(function(param) {
|
||||
var element = $('#' + param);
|
||||
const element = $('#' + param);
|
||||
if (element.length) {
|
||||
var value = element.val();
|
||||
const value = element.val();
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
value.forEach(function(v) {
|
||||
params.append(param + '[]', v);
|
||||
@@ -56,8 +56,8 @@ $filter_select_id = $options['filter_select_id'] ?? 'filters';
|
||||
});
|
||||
|
||||
// Update URL without page reload
|
||||
var new_url = window.location.pathname;
|
||||
var params_str = params.toString();
|
||||
const new_url = window.location.pathname;
|
||||
const params_str = params.toString();
|
||||
if (params_str) {
|
||||
new_url += '?' + params_str;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ function safeRemoveItem(key) {
|
||||
}
|
||||
|
||||
// Load saved column visibility from localStorage
|
||||
var savedVisibility = JSON.parse(safeGetItem('columnVisibility')) || { cost: false, profit: false };
|
||||
var visibleColumns = savedVisibility;
|
||||
const savedVisibility = JSON.parse(safeGetItem('columnVisibility')) || { cost: false, profit: false };
|
||||
let visibleColumns = savedVisibility;
|
||||
|
||||
// Function to save column visibility to localStorage
|
||||
function saveColumnVisibility(visibility) {
|
||||
@@ -56,13 +56,13 @@ $('#table').bootstrapTable('refreshOptions', {
|
||||
});
|
||||
|
||||
// Initialize visibility settings from localStorage
|
||||
var summaryVisibility = JSON.parse(safeGetItem('summaryVisibility')) || { cost: false, profit: false };
|
||||
let summaryVisibility = JSON.parse(safeGetItem('summaryVisibility')) || { cost: false, profit: false };
|
||||
|
||||
// Function to apply visibility for cost and profit rows
|
||||
function applySummaryVisibility() {
|
||||
var rows = $('#report_summary .summary_row');
|
||||
var costRow = rows.eq(rows.length - 2); // Second-to-last row
|
||||
var profitRow = rows.eq(rows.length - 1); // Last row
|
||||
const rows = $('#report_summary .summary_row');
|
||||
const costRow = rows.eq(rows.length - 2); // Second-to-last row
|
||||
const profitRow = rows.eq(rows.length - 1); // Last row
|
||||
|
||||
if (summaryVisibility.cost === false) {
|
||||
costRow.hide(); // Hide the cost row
|
||||
@@ -90,7 +90,7 @@ $('#toggleCostProfitButton').click(function () {
|
||||
applySummaryVisibility();
|
||||
|
||||
// Initialize dialog (if editable)
|
||||
var init_dialog = function () {
|
||||
const init_dialog = function () {
|
||||
<?php if (isset($editable)): ?>
|
||||
table_support.submit_handler('<?php echo site_url("reports/get_detailed_{$editable}_row") ?>');
|
||||
dialog_support.init("a.modal-dlg");
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
pageSize: <?= $config['lines_per_page'] ?>,
|
||||
uniqueId: 'people.person_id',
|
||||
enableActions: function() {
|
||||
var email_disabled = $("td input:checkbox:checked").parents("tr").find("td a[href^='mailto:']").length == 0;
|
||||
const email_disabled = $("td input:checkbox:checked").parents("tr").find("td a[href^='mailto:']").length == 0;
|
||||
$("#email").prop('disabled', email_disabled);
|
||||
}
|
||||
});
|
||||
|
||||
$("#email").click(function(event) {
|
||||
var recipients = $.map($("tr.selected a[href^='mailto:']"), function(element) {
|
||||
const recipients = $.map($("tr.selected a[href^='mailto:']"), function(element) {
|
||||
return $(element).attr('href').replace(/^mailto:/, '');
|
||||
});
|
||||
location.href = "mailto:" + recipients.join(",");
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
$('#datetime').datetimepicker(pickerconfig);
|
||||
|
||||
var fill_value = function(event, ui) {
|
||||
const fill_value = function(event, ui) {
|
||||
event.preventDefault();
|
||||
$("input[name='supplier_id']").val(ui.item.value);
|
||||
$("input[name='supplier_name']").val(ui.item.label);
|
||||
|
||||
@@ -533,7 +533,7 @@ if (isset($success)) {
|
||||
});
|
||||
|
||||
$('[name="discount_toggle"]').change(function() {
|
||||
var input = $("<input>").attr("type", "hidden").attr("name", "discount_type").val(($(this).prop('checked')) ? 1 : 0);
|
||||
const input = $("<input>").attr("type", "hidden").attr("name", "discount_type").val(($(this).prop('checked')) ? 1 : 0);
|
||||
$('#cart_' + $(this).attr('data-line')).append($(input));
|
||||
$('#cart_' + $(this).attr('data-line')).submit();
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
// Labels and data series
|
||||
var data = {
|
||||
const data = {
|
||||
labels: <?= esc(json_encode($labels_1), 'js') ?>,
|
||||
series: [{
|
||||
name: '<?= esc($yaxis_title, 'js') ?>',
|
||||
@@ -20,7 +20,7 @@
|
||||
};
|
||||
|
||||
// We are setting a few options for our chart and override the defaults
|
||||
var options = {
|
||||
const options = {
|
||||
|
||||
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
|
||||
width: '100%',
|
||||
@@ -98,7 +98,7 @@
|
||||
]
|
||||
};
|
||||
|
||||
var responsiveOptions = [
|
||||
const responsiveOptions = [
|
||||
['screen and (min-width: 640px)', {
|
||||
height: '80%',
|
||||
chartPadding: {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
// Labels and data series
|
||||
var data = {
|
||||
const data = {
|
||||
labels: <?= json_encode(esc($labels_1, 'js')) ?>,
|
||||
series: [{
|
||||
name: '<?= esc($yaxis_title, 'js') ?>',
|
||||
@@ -20,7 +20,7 @@
|
||||
};
|
||||
|
||||
// We are setting a few options for our chart and override the defaults
|
||||
var options = {
|
||||
const options = {
|
||||
|
||||
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
|
||||
width: '100%',
|
||||
@@ -101,7 +101,7 @@
|
||||
]
|
||||
};
|
||||
|
||||
var responsiveOptions = [
|
||||
const responsiveOptions = [
|
||||
['screen and (min-width: 640px)', {
|
||||
height: '80%',
|
||||
chartPadding: {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
// Labels and data series
|
||||
var data = {
|
||||
const data = {
|
||||
labels: <?= json_encode(esc($labels_1, 'js')) ?>,
|
||||
series: [{
|
||||
name: '<?= esc($yaxis_title, 'js') ?>',
|
||||
@@ -20,7 +20,7 @@
|
||||
};
|
||||
|
||||
// We are setting a few options for our chart and override the defaults
|
||||
var options = {
|
||||
const options = {
|
||||
|
||||
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
|
||||
width: '100%',
|
||||
@@ -150,7 +150,7 @@
|
||||
]
|
||||
};
|
||||
|
||||
var responsiveOptions = [
|
||||
const responsiveOptions = [
|
||||
['screen and (min-width: 640px)', {
|
||||
height: '80%',
|
||||
chartPadding: {
|
||||
@@ -172,7 +172,7 @@
|
||||
// If the draw event was triggered from drawing a point on the line chart
|
||||
if (data.type === 'point') {
|
||||
// We are creating a new path SVG element that draws a triangle around the point coordinates
|
||||
var circle = new Chartist.Svg('circle', {
|
||||
const circle = new Chartist.Svg('circle', {
|
||||
cx: [data.x],
|
||||
cy: [data.y],
|
||||
r: [5],
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
// Labels and data series
|
||||
var data = {
|
||||
const data = {
|
||||
labels: <?= json_encode(esc($labels_1, 'js')) ?>,
|
||||
series: <?= json_encode(esc($series_data_1, 'js')) ?>
|
||||
};
|
||||
|
||||
// We are setting a few options for our chart and override the defaults
|
||||
var options = {
|
||||
const options = {
|
||||
|
||||
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
|
||||
width: '100%',
|
||||
@@ -53,7 +53,7 @@
|
||||
})
|
||||
] };
|
||||
|
||||
var responsiveOptions = [
|
||||
const responsiveOptions = [
|
||||
['screen and (min-width: 640px)', {
|
||||
height: '80%',
|
||||
chartPadding: 20
|
||||
@@ -72,9 +72,9 @@
|
||||
// Generate random colours for the pie sliced because Chartist is currently limited to 15 colours
|
||||
chart.on('draw', function(data) {
|
||||
if (data.type === 'slice') {
|
||||
var r = Math.floor(Math.random() * 256);
|
||||
var g = Math.floor(Math.random() * 256);
|
||||
var b = Math.floor(Math.random() * 256);
|
||||
const r = Math.floor(Math.random() * 256);
|
||||
const g = Math.floor(Math.random() * 256);
|
||||
const b = Math.floor(Math.random() * 256);
|
||||
|
||||
data.element.attr({
|
||||
style: 'fill: #' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user