mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-05-25 08:44:42 -04:00
Compare commits
4 Commits
feature/ub
...
WebShells-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a6cfffc27 | ||
|
|
5e0541c53e | ||
|
|
b0dddc22a3 | ||
|
|
8d6b166673 |
219
.github/workflows/deploy-core.yml
vendored
Normal file
219
.github/workflows/deploy-core.yml
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
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
Normal file
79
.github/workflows/deploy-pr.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
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
Normal file
23
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
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
|
||||
@@ -5,6 +5,7 @@ namespace Config;
|
||||
use App\Models\Appconfig;
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Config\Database;
|
||||
|
||||
/**
|
||||
* This class holds the configuration options stored from the database so that on launch those settings can be cached
|
||||
@@ -13,7 +14,7 @@ use CodeIgniter\Config\BaseConfig;
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -33,26 +34,35 @@ class OSPOS extends BaseConfig
|
||||
|
||||
if ($cache) {
|
||||
$this->settings = decode_array($cache);
|
||||
} 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 (\Exception $e) {
|
||||
// Database table doesn't exist yet (migrations haven't run)
|
||||
// or database connection failed. Return empty settings to
|
||||
// allow migration page to display. Catches mysqli_sql_exception
|
||||
// which is not a subclass of DatabaseException.
|
||||
$this->settings = [
|
||||
'language' => 'english',
|
||||
'language_code' => 'en',
|
||||
'company' => 'Home',
|
||||
'barcode_type' => 'Code39'
|
||||
];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::connect();
|
||||
|
||||
if (!$db->tableExists('app_config')) {
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
$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'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,4 +73,4 @@ class OSPOS extends BaseConfig
|
||||
$this->cache->delete('settings');
|
||||
$this->set_settings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
|
||||
@@ -12,6 +12,40 @@ $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');
|
||||
|
||||
@@ -39,4 +73,4 @@ $routes->add('reports/specific_(:any)/(:any)/(:any)/(:any)', 'Reports::Specific_
|
||||
$routes->add('reports/specific_customers', 'Reports::specific_customer_input');
|
||||
$routes->add('reports/specific_employees', 'Reports::specific_employee_input');
|
||||
$routes->add('reports/specific_discounts', 'Reports::specific_discount_input');
|
||||
$routes->add('reports/specific_suppliers', 'Reports::specific_supplier_input');
|
||||
$routes->add('reports/specific_suppliers', 'Reports::specific_supplier_input');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,13 @@ 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' => [
|
||||
@@ -62,13 +69,6 @@ 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()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class Migration_Upgrade_To_3_1_1 extends Migration
|
||||
@@ -17,7 +18,37 @@ class Migration_Upgrade_To_3_1_1 extends Migration
|
||||
public function up(): void
|
||||
{
|
||||
helper('migration');
|
||||
execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql');
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -327,19 +327,6 @@ 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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
|
||||
-- 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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
|
||||
-- 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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
-- 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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
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 AUTO_INCREMENT=1;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci 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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
-- 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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
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
|
||||
|
||||
@@ -272,9 +272,6 @@ function get_payment_options(): array
|
||||
$payments[lang('Sales.upi')] = lang('Sales.upi');
|
||||
}
|
||||
|
||||
$payments[lang('Sales.bank_transfer')] = lang('Sales.bank_transfer');
|
||||
$payments[lang('Sales.wallet')] = lang('Sales.wallet');
|
||||
|
||||
return $payments;
|
||||
}
|
||||
|
||||
@@ -368,6 +365,74 @@ 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,6 +172,7 @@ 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 = [];
|
||||
|
||||
@@ -1,336 +1,344 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"address" => "Company Address",
|
||||
"address_required" => "Company address is a required field.",
|
||||
"all_set" => "All file permissions are set correctly!",
|
||||
"allow_duplicate_barcodes" => "Allow Duplicate Barcodes",
|
||||
"apostrophe" => "apostrophe",
|
||||
"backup_button" => "Backup",
|
||||
"backup_database" => "Backup Database",
|
||||
"barcode" => "Barcode",
|
||||
"barcode_company" => "Company Name",
|
||||
"barcode_configuration" => "Barcode Configuration",
|
||||
"barcode_content" => "Barcode Content",
|
||||
"barcode_first_row" => "Row 1",
|
||||
"barcode_font" => "Font",
|
||||
"barcode_formats" => "Input Formats",
|
||||
"barcode_generate_if_empty" => "Generate if empty.",
|
||||
"barcode_height" => "Height (px)",
|
||||
"barcode_id" => "Item Id/Name",
|
||||
"barcode_info" => "Barcode Configuration Information",
|
||||
"barcode_layout" => "Barcode Layout",
|
||||
"barcode_name" => "Name",
|
||||
"barcode_number" => "Barcode",
|
||||
"barcode_number_in_row" => "Number in row",
|
||||
"barcode_page_cellspacing" => "Display page cellspacing.",
|
||||
"barcode_page_width" => "Display page width",
|
||||
"barcode_price" => "Price",
|
||||
"barcode_second_row" => "Row 2",
|
||||
"barcode_third_row" => "Row 3",
|
||||
"barcode_tooltip" => "Warning: This feature can cause duplicate items to be imported or created. Do not use if you do not want duplicate barcodes.",
|
||||
"barcode_type" => "Barcode Type",
|
||||
"barcode_width" => "Width (px)",
|
||||
"bottom" => "Bottom",
|
||||
"cash_button" => "",
|
||||
"cash_button_1" => "",
|
||||
"cash_button_2" => "",
|
||||
"cash_button_3" => "",
|
||||
"cash_button_4" => "",
|
||||
"cash_button_5" => "",
|
||||
"cash_button_6" => "",
|
||||
"cash_decimals" => "Cash Decimals",
|
||||
"cash_decimals_tooltip" => "If Cash Decimals and Currency Decimals are the same then no cash triggered rounding will take place, unless Cash Rounding is set to Half Five.",
|
||||
"cash_rounding" => "Cash Rounding",
|
||||
"category_dropdown" => "Show Category as a dropdown",
|
||||
"center" => "Center",
|
||||
"change_apperance_tooltip" => "",
|
||||
"comma" => "comma",
|
||||
"company" => "Company Name",
|
||||
"company_avatar" => "",
|
||||
"company_change_image" => "Change Image",
|
||||
"company_logo" => "Company Logo",
|
||||
"company_remove_image" => "Remove Image",
|
||||
"company_required" => "Company name is a required field",
|
||||
"company_select_image" => "Select Image",
|
||||
"company_website_url" => "Company website is not a valid URL (http://...).",
|
||||
"country_codes" => "Country Codes",
|
||||
"country_codes_tooltip" => "Comma separated list of country codes for nominatim address lookup.",
|
||||
"currency_code" => "Currency Code",
|
||||
"currency_decimals" => "Currency Decimals",
|
||||
"currency_symbol" => "Currency Symbol",
|
||||
"current_employee_only" => "",
|
||||
"customer_reward" => "Reward",
|
||||
"customer_reward_duplicate" => "Reward must be unique.",
|
||||
"customer_reward_enable" => "Enable Customer Rewards",
|
||||
"customer_reward_invalid_chars" => "Reward can not contain '_'",
|
||||
"customer_reward_required" => "Reward is a required field",
|
||||
"customer_sales_tax_support" => "",
|
||||
"date_or_time_format" => "Date and Time Filter",
|
||||
"datetimeformat" => "Date and Time Format",
|
||||
"decimal_point" => "Decimal Point",
|
||||
"default_barcode_font_size_number" => "Default Barcode Font Size must be a number.",
|
||||
"default_barcode_font_size_required" => "Default Barcode Font Size is a required field.",
|
||||
"default_barcode_height_number" => "Default Barcode Height must be a number.",
|
||||
"default_barcode_height_required" => "Default Barcode Height is a required field.",
|
||||
"default_barcode_num_in_row_number" => "Default Barcode Number in Row must be a number.",
|
||||
"default_barcode_num_in_row_required" => "Default Barcode Number in Row is a required field.",
|
||||
"default_barcode_page_cellspacing_number" => "Default Barcode Page Cellspacing must be a number.",
|
||||
"default_barcode_page_cellspacing_required" => "Default Barcode Page Cellspacing is a required field.",
|
||||
"default_barcode_page_width_number" => "Default Barcode Page Width must be a number.",
|
||||
"default_barcode_page_width_required" => "Default Barcode Page Width is a required field.",
|
||||
"default_barcode_width_number" => "Default Barcode Width must be a number.",
|
||||
"default_barcode_width_required" => "Default Barcode Width is a required field.",
|
||||
"default_item_columns" => "Default Visible Item Columns",
|
||||
"default_origin_tax_code" => "Default Origin Tax Code",
|
||||
"default_receivings_discount" => "Default Receivings Discount",
|
||||
"default_receivings_discount_number" => "Default Receivings Discount must be a number.",
|
||||
"default_receivings_discount_required" => "Default Receivings Discount is a required field.",
|
||||
"default_sales_discount" => "Default Sales Discount",
|
||||
"default_sales_discount_number" => "Default Sales Discount must be a number.",
|
||||
"default_sales_discount_required" => "Default Sales Discount is a required field.",
|
||||
"default_tax_category" => "Default Tax Category",
|
||||
"default_tax_code" => "Default Tax Code",
|
||||
"default_tax_jurisdiction" => "Default Tax Jurisdiction",
|
||||
"default_tax_name_number" => "Default Tax Name must be a string.",
|
||||
"default_tax_name_required" => "Default Tax Name is a required field.",
|
||||
"default_tax_rate" => "Default Tax Rate %",
|
||||
"default_tax_rate_1" => "Tax 1 Rate",
|
||||
"default_tax_rate_2" => "Tax 2 Rate",
|
||||
"default_tax_rate_3" => "",
|
||||
"default_tax_rate_number" => "Default Tax Rate must be a number.",
|
||||
"default_tax_rate_required" => "Default Tax Rate is a required field.",
|
||||
"derive_sale_quantity" => "Allow Derived Sale Quantity",
|
||||
"derive_sale_quantity_tooltip" => "If checked then a new item type will be provided for items ordered by extended amount",
|
||||
"dinner_table" => "Table",
|
||||
"dinner_table_duplicate" => "Table must be unique.",
|
||||
"dinner_table_enable" => "Enable Dinner Tables",
|
||||
"dinner_table_invalid_chars" => "Table Name can not contain '_'.",
|
||||
"dinner_table_required" => "Table is a required field.",
|
||||
"dot" => "dot",
|
||||
"email" => "Email",
|
||||
"email_configuration" => "Email Configuration",
|
||||
"email_mailpath" => "Path to Sendmail",
|
||||
"email_protocol" => "Protocol",
|
||||
"email_receipt_check_behaviour" => "Email Receipt checkbox",
|
||||
"email_receipt_check_behaviour_always" => "Always checked",
|
||||
"email_receipt_check_behaviour_last" => "Remember last selection",
|
||||
"email_receipt_check_behaviour_never" => "Always unchecked",
|
||||
"email_smtp_crypto" => "SMTP Encryption",
|
||||
"email_smtp_host" => "SMTP Server",
|
||||
"email_smtp_pass" => "SMTP Password",
|
||||
"email_smtp_port" => "SMTP Port",
|
||||
"email_smtp_timeout" => "SMTP Timeout (s)",
|
||||
"email_smtp_user" => "SMTP Username",
|
||||
"enable_avatar" => "",
|
||||
"enable_avatar_tooltip" => "",
|
||||
"enable_dropdown_tooltip" => "",
|
||||
"enable_new_look" => "",
|
||||
"enable_right_bar" => "",
|
||||
"enable_right_bar_tooltip" => "",
|
||||
"enforce_privacy" => "Enforce privacy",
|
||||
"enforce_privacy_tooltip" => "Protect Customers privacy enforcing data scrambling in case of their data being deleted",
|
||||
"fax" => "Fax",
|
||||
"file_perm" => "There are problems with file permissions. Please fix and reload this page.",
|
||||
"financial_year" => "Fiscal Year Start",
|
||||
"financial_year_apr" => "1st of April",
|
||||
"financial_year_aug" => "1st of August",
|
||||
"financial_year_dec" => "1st of December",
|
||||
"financial_year_feb" => "1st of February",
|
||||
"financial_year_jan" => "1st of January",
|
||||
"financial_year_jul" => "1st of July",
|
||||
"financial_year_jun" => "1st of June",
|
||||
"financial_year_mar" => "1st of March",
|
||||
"financial_year_may" => "1st of May",
|
||||
"financial_year_nov" => "1st of November",
|
||||
"financial_year_oct" => "1st of October",
|
||||
"financial_year_sep" => "1st of September",
|
||||
"floating_labels" => "Floating Labels",
|
||||
"gcaptcha_enable" => "Login Page reCAPTCHA",
|
||||
"gcaptcha_secret_key" => "reCAPTCHA Secret Key",
|
||||
"gcaptcha_secret_key_required" => "reCAPTCHA Secret Key is a required field",
|
||||
"gcaptcha_site_key" => "reCAPTCHA Site Key",
|
||||
"gcaptcha_site_key_required" => "reCAPTCHA Site Key is a required field",
|
||||
"gcaptcha_tooltip" => "Protect the Login page with Google reCAPTCHA, click the icon for an API key pair.",
|
||||
"general" => "General",
|
||||
"general_configuration" => "General Configuration",
|
||||
"giftcard_number" => "Gift Card Number",
|
||||
"giftcard_random" => "Generate Random",
|
||||
"giftcard_series" => "Generate in Series",
|
||||
"image_allowed_file_types" => "Allowed file types",
|
||||
"image_max_height_tooltip" => "Maximum allowed height of image uploads in pixels (px).",
|
||||
"image_max_size_tooltip" => "Maximum allowed file size of image uploads in kilobytes (kb).",
|
||||
"image_max_width_tooltip" => "Maximum allowed width of image uploads in pixels (px).",
|
||||
"image_restrictions" => "Image Upload Restrictions",
|
||||
"include_hsn" => "Include Support for HSN Codes",
|
||||
"info" => "Information",
|
||||
"info_configuration" => "Store Information",
|
||||
"input_groups" => "Input Groups",
|
||||
"integrations" => "Integrations",
|
||||
"integrations_configuration" => "Third Party Integrations",
|
||||
"invoice" => "Invoice",
|
||||
"invoice_configuration" => "Invoice Print Settings",
|
||||
"invoice_default_comments" => "Default Invoice Comments",
|
||||
"invoice_email_message" => "Invoice Email Template",
|
||||
"invoice_enable" => "Enable Invoicing",
|
||||
"invoice_printer" => "Invoice Printer",
|
||||
"invoice_type" => "Invoice Type",
|
||||
"is_readable" => "is readable, but the permissions are incorrectly set. Please set it to 640 or 660 and refresh.",
|
||||
"is_writable" => "is writable, but the permissions are incorrectly set. Please set it to 750 and refresh.",
|
||||
"item_markup" => "",
|
||||
"jsprintsetup_required" => "Warning: This functionality will only work if you have the FireFox jsPrintSetup addon installed. Save anyway?",
|
||||
"language" => "Language",
|
||||
"last_used_invoice_number" => "Last used Invoice Number",
|
||||
"last_used_quote_number" => "Last used Quote Number",
|
||||
"last_used_work_order_number" => "Last used W/O Number",
|
||||
"left" => "Left",
|
||||
"license" => "License",
|
||||
"license_configuration" => "License Statement",
|
||||
"line_sequence" => "Line Sequence",
|
||||
"lines_per_page" => "Lines per Page",
|
||||
"lines_per_page_number" => "Lines per Page must be a number.",
|
||||
"lines_per_page_required" => "Lines per Page is a required field.",
|
||||
"locale" => "Localization",
|
||||
"locale_configuration" => "Localization Configuration",
|
||||
"locale_info" => "Localization Configuration Information",
|
||||
"location" => "Stock",
|
||||
"location_configuration" => "Stock Locations",
|
||||
"location_info" => "Location Configuration Information",
|
||||
"login_form" => "Login Form Style",
|
||||
"logout" => "Do you want to make a backup before logging out? Click [OK] to backup or [Cancel] to logout.",
|
||||
"mailchimp" => "MailChimp",
|
||||
"mailchimp_api_key" => "MailChimp API Key",
|
||||
"mailchimp_configuration" => "MailChimp Configuration",
|
||||
"mailchimp_key_successfully" => "API Key is valid.",
|
||||
"mailchimp_key_unsuccessfully" => "API Key is invalid.",
|
||||
"mailchimp_lists" => "MailChimp List(s)",
|
||||
"mailchimp_tooltip" => "Click the icon for an API Key.",
|
||||
"message" => "Message",
|
||||
"message_configuration" => "Message Configuration",
|
||||
"msg_msg" => "Saved Text Message",
|
||||
"msg_msg_placeholder" => "If you wish to use a SMS template save your message here, otherwise leave the box blank.",
|
||||
"msg_pwd" => "SMS-API Password",
|
||||
"msg_pwd_required" => "SMS-API Password is a required field",
|
||||
"msg_src" => "SMS-API Sender ID",
|
||||
"msg_src_required" => "SMS-API Sender ID is a required field",
|
||||
"msg_uid" => "SMS-API Username",
|
||||
"msg_uid_required" => "SMS-API Username is a required field",
|
||||
"multi_pack_enabled" => "Multiple Packages per Item",
|
||||
"no_risk" => "No security/vulnerability risks.",
|
||||
"none" => "none",
|
||||
"notify_alignment" => "Notification Popup Position",
|
||||
"number_format" => "Number Format",
|
||||
"number_locale" => "Localization",
|
||||
"number_locale_invalid" => "The entered locale is invalid. Check the link in the tooltip to find a valid locale.",
|
||||
"number_locale_required" => "Number Locale is a required field.",
|
||||
"number_locale_tooltip" => "Find a suitable locale through this link.",
|
||||
"os_timezone" => "OSPOS Timezone:",
|
||||
"ospos_info" => "OSPOS Installation Info",
|
||||
"payment_options_order" => "Payment Options Order",
|
||||
"perm_risk" => "Incorrect permissions leaves this software at risk.",
|
||||
"phone" => "Company Phone",
|
||||
"phone_required" => "Company Phone is a required field.",
|
||||
"print_bottom_margin" => "Margin Bottom",
|
||||
"print_bottom_margin_number" => "Margin Bottom must be a number.",
|
||||
"print_bottom_margin_required" => "Margin Bottom is a required field.",
|
||||
"print_delay_autoreturn" => "Autoreturn to Sale delay",
|
||||
"print_delay_autoreturn_number" => "Autoreturn to Sale delay is a required field.",
|
||||
"print_delay_autoreturn_required" => "Autoreturn to Sale delay must be a number.",
|
||||
"print_footer" => "Print Browser Footer",
|
||||
"print_header" => "Print Browser Header",
|
||||
"print_left_margin" => "Margin Left",
|
||||
"print_left_margin_number" => "Margin Left must be a number.",
|
||||
"print_left_margin_required" => "Margin Left is a required field.",
|
||||
"print_receipt_check_behaviour" => "Print Receipt checkbox",
|
||||
"print_receipt_check_behaviour_always" => "Always checked",
|
||||
"print_receipt_check_behaviour_last" => "Remember last selection",
|
||||
"print_receipt_check_behaviour_never" => "Always unchecked",
|
||||
"print_right_margin" => "Margin Right",
|
||||
"print_right_margin_number" => "Margin Right must be a number.",
|
||||
"print_right_margin_required" => "Margin Right is a required field.",
|
||||
"print_silently" => "Show Print Dialog",
|
||||
"print_top_margin" => "Margin Top",
|
||||
"print_top_margin_number" => "Margin Top must be a number.",
|
||||
"print_top_margin_required" => "Margin Top is a required field.",
|
||||
"quantity_decimals" => "Quantity Decimals",
|
||||
"quick_cash_enable" => "",
|
||||
"quote_default_comments" => "Default Quote Comments",
|
||||
"receipt" => "Receipt",
|
||||
"receipt_category" => "",
|
||||
"receipt_configuration" => "Receipt Print Settings",
|
||||
"receipt_default" => "Default",
|
||||
"receipt_font_size" => "Font Size",
|
||||
"receipt_font_size_number" => "Font Size must be a number.",
|
||||
"receipt_font_size_required" => "Font Size is a required field.",
|
||||
"receipt_info" => "Receipt Configuration Information",
|
||||
"receipt_printer" => "Ticket Printer",
|
||||
"receipt_short" => "Short",
|
||||
<?php
|
||||
|
||||
return [
|
||||
"address" => "Company Address",
|
||||
"address_required" => "Company address is a required field.",
|
||||
"all_set" => "All file permissions are set correctly!",
|
||||
"allow_duplicate_barcodes" => "Allow Duplicate Barcodes",
|
||||
"apostrophe" => "apostrophe",
|
||||
"backup_button" => "Backup",
|
||||
"backup_database" => "Backup Database",
|
||||
"barcode" => "Barcode",
|
||||
"barcode_company" => "Company Name",
|
||||
"barcode_configuration" => "Barcode Configuration",
|
||||
"barcode_content" => "Barcode Content",
|
||||
"barcode_first_row" => "Row 1",
|
||||
"barcode_font" => "Font",
|
||||
"barcode_formats" => "Input Formats",
|
||||
"barcode_generate_if_empty" => "Generate if empty.",
|
||||
"barcode_height" => "Height (px)",
|
||||
"barcode_id" => "Item Id/Name",
|
||||
"barcode_info" => "Barcode Configuration Information",
|
||||
"barcode_layout" => "Barcode Layout",
|
||||
"barcode_name" => "Name",
|
||||
"barcode_number" => "Barcode",
|
||||
"barcode_number_in_row" => "Number in row",
|
||||
"barcode_page_cellspacing" => "Display page cellspacing.",
|
||||
"barcode_page_width" => "Display page width",
|
||||
"barcode_price" => "Price",
|
||||
"barcode_second_row" => "Row 2",
|
||||
"barcode_third_row" => "Row 3",
|
||||
"barcode_tooltip" => "Warning: This feature can cause duplicate items to be imported or created. Do not use if you do not want duplicate barcodes.",
|
||||
"barcode_type" => "Barcode Type",
|
||||
"barcode_width" => "Width (px)",
|
||||
"bottom" => "Bottom",
|
||||
"cash_button" => "",
|
||||
"cash_button_1" => "",
|
||||
"cash_button_2" => "",
|
||||
"cash_button_3" => "",
|
||||
"cash_button_4" => "",
|
||||
"cash_button_5" => "",
|
||||
"cash_button_6" => "",
|
||||
"cash_decimals" => "Cash Decimals",
|
||||
"cash_decimals_tooltip" => "If Cash Decimals and Currency Decimals are the same then no cash triggered rounding will take place, unless Cash Rounding is set to Half Five.",
|
||||
"cash_rounding" => "Cash Rounding",
|
||||
"category_dropdown" => "Show Category as a dropdown",
|
||||
"center" => "Center",
|
||||
"change_apperance_tooltip" => "",
|
||||
"comma" => "comma",
|
||||
"company" => "Company Name",
|
||||
"company_avatar" => "",
|
||||
"company_change_image" => "Change Image",
|
||||
"company_logo" => "Company Logo",
|
||||
"company_remove_image" => "Remove Image",
|
||||
"company_required" => "Company name is a required field",
|
||||
"company_select_image" => "Select Image",
|
||||
"company_website_url" => "Company website is not a valid URL (http://...).",
|
||||
"country_codes" => "Country Codes",
|
||||
"country_codes_tooltip" => "Comma separated list of country codes for nominatim address lookup.",
|
||||
"currency_code" => "Currency Code",
|
||||
"currency_decimals" => "Currency Decimals",
|
||||
"currency_symbol" => "Currency Symbol",
|
||||
"current_employee_only" => "",
|
||||
"customer_reward" => "Reward",
|
||||
"customer_reward_duplicate" => "Reward must be unique.",
|
||||
"customer_reward_enable" => "Enable Customer Rewards",
|
||||
"customer_reward_invalid_chars" => "Reward can not contain '_'",
|
||||
"customer_reward_required" => "Reward is a required field",
|
||||
"customer_sales_tax_support" => "",
|
||||
"date_or_time_format" => "Date and Time Filter",
|
||||
"datetimeformat" => "Date and Time Format",
|
||||
"decimal_point" => "Decimal Point",
|
||||
"default_barcode_font_size_number" => "Default Barcode Font Size must be a number.",
|
||||
"default_barcode_font_size_required" => "Default Barcode Font Size is a required field.",
|
||||
"default_barcode_height_number" => "Default Barcode Height must be a number.",
|
||||
"default_barcode_height_required" => "Default Barcode Height is a required field.",
|
||||
"default_barcode_num_in_row_number" => "Default Barcode Number in Row must be a number.",
|
||||
"default_barcode_num_in_row_required" => "Default Barcode Number in Row is a required field.",
|
||||
"default_barcode_page_cellspacing_number" => "Default Barcode Page Cellspacing must be a number.",
|
||||
"default_barcode_page_cellspacing_required" => "Default Barcode Page Cellspacing is a required field.",
|
||||
"default_barcode_page_width_number" => "Default Barcode Page Width must be a number.",
|
||||
"default_barcode_page_width_required" => "Default Barcode Page Width is a required field.",
|
||||
"default_barcode_width_number" => "Default Barcode Width must be a number.",
|
||||
"default_barcode_width_required" => "Default Barcode Width is a required field.",
|
||||
"default_item_columns" => "Default Visible Item Columns",
|
||||
"default_origin_tax_code" => "Default Origin Tax Code",
|
||||
"default_receivings_discount" => "Default Receivings Discount",
|
||||
"default_receivings_discount_number" => "Default Receivings Discount must be a number.",
|
||||
"default_receivings_discount_required" => "Default Receivings Discount is a required field.",
|
||||
"default_sales_discount" => "Default Sales Discount",
|
||||
"default_sales_discount_number" => "Default Sales Discount must be a number.",
|
||||
"default_sales_discount_required" => "Default Sales Discount is a required field.",
|
||||
"default_tax_category" => "Default Tax Category",
|
||||
"default_tax_code" => "Default Tax Code",
|
||||
"default_tax_jurisdiction" => "Default Tax Jurisdiction",
|
||||
"default_tax_name_number" => "Default Tax Name must be a string.",
|
||||
"default_tax_name_required" => "Default Tax Name is a required field.",
|
||||
"default_tax_rate" => "Default Tax Rate %",
|
||||
"default_tax_rate_1" => "Tax 1 Rate",
|
||||
"default_tax_rate_2" => "Tax 2 Rate",
|
||||
"default_tax_rate_3" => "",
|
||||
"default_tax_rate_number" => "Default Tax Rate must be a number.",
|
||||
"default_tax_rate_required" => "Default Tax Rate is a required field.",
|
||||
"derive_sale_quantity" => "Allow Derived Sale Quantity",
|
||||
"derive_sale_quantity_tooltip" => "If checked then a new item type will be provided for items ordered by extended amount",
|
||||
"dinner_table" => "Table",
|
||||
"dinner_table_duplicate" => "Table must be unique.",
|
||||
"dinner_table_enable" => "Enable Dinner Tables",
|
||||
"dinner_table_invalid_chars" => "Table Name can not contain '_'.",
|
||||
"dinner_table_required" => "Table is a required field.",
|
||||
"dot" => "dot",
|
||||
"email" => "Email",
|
||||
"email_configuration" => "Email Configuration",
|
||||
"email_mailpath" => "Path to Sendmail",
|
||||
"email_protocol" => "Protocol",
|
||||
"email_receipt_check_behaviour" => "Email Receipt checkbox",
|
||||
"email_receipt_check_behaviour_always" => "Always checked",
|
||||
"email_receipt_check_behaviour_last" => "Remember last selection",
|
||||
"email_receipt_check_behaviour_never" => "Always unchecked",
|
||||
"email_smtp_crypto" => "SMTP Encryption",
|
||||
"email_smtp_host" => "SMTP Server",
|
||||
"email_smtp_pass" => "SMTP Password",
|
||||
"email_smtp_port" => "SMTP Port",
|
||||
"email_smtp_timeout" => "SMTP Timeout (s)",
|
||||
"email_smtp_user" => "SMTP Username",
|
||||
"enable_avatar" => "",
|
||||
"enable_avatar_tooltip" => "",
|
||||
"enable_dropdown_tooltip" => "",
|
||||
"enable_new_look" => "",
|
||||
"enable_right_bar" => "",
|
||||
"enable_right_bar_tooltip" => "",
|
||||
"enforce_privacy" => "Enforce privacy",
|
||||
"enforce_privacy_tooltip" => "Protect Customers privacy enforcing data scrambling in case of their data being deleted",
|
||||
"fax" => "Fax",
|
||||
"file_perm" => "There are problems with file permissions. Please fix and reload this page.",
|
||||
"financial_year" => "Fiscal Year Start",
|
||||
"financial_year_apr" => "1st of April",
|
||||
"financial_year_aug" => "1st of August",
|
||||
"financial_year_dec" => "1st of December",
|
||||
"financial_year_feb" => "1st of February",
|
||||
"financial_year_jan" => "1st of January",
|
||||
"financial_year_jul" => "1st of July",
|
||||
"financial_year_jun" => "1st of June",
|
||||
"financial_year_mar" => "1st of March",
|
||||
"financial_year_may" => "1st of May",
|
||||
"financial_year_nov" => "1st of November",
|
||||
"financial_year_oct" => "1st of October",
|
||||
"financial_year_sep" => "1st of September",
|
||||
"floating_labels" => "Floating Labels",
|
||||
"gcaptcha_enable" => "Login Page reCAPTCHA",
|
||||
"gcaptcha_secret_key" => "reCAPTCHA Secret Key",
|
||||
"gcaptcha_secret_key_required" => "reCAPTCHA Secret Key is a required field",
|
||||
"gcaptcha_site_key" => "reCAPTCHA Site Key",
|
||||
"gcaptcha_site_key_required" => "reCAPTCHA Site Key is a required field",
|
||||
"gcaptcha_tooltip" => "Protect the Login page with Google reCAPTCHA, click the icon for an API key pair.",
|
||||
"general" => "General",
|
||||
"general_configuration" => "General Configuration",
|
||||
"giftcard_number" => "Gift Card Number",
|
||||
"giftcard_random" => "Generate Random",
|
||||
"giftcard_series" => "Generate in Series",
|
||||
"image_allowed_file_types" => "Allowed file types",
|
||||
"image_max_height_tooltip" => "Maximum allowed height of image uploads in pixels (px).",
|
||||
"image_max_size_tooltip" => "Maximum allowed file size of image uploads in kilobytes (kb).",
|
||||
"image_max_width_tooltip" => "Maximum allowed width of image uploads in pixels (px).",
|
||||
"image_restrictions" => "Image Upload Restrictions",
|
||||
"include_hsn" => "Include Support for HSN Codes",
|
||||
"info" => "Information",
|
||||
"info_configuration" => "Store Information",
|
||||
"input_groups" => "Input Groups",
|
||||
"integrations" => "Integrations",
|
||||
"integrations_configuration" => "Third Party Integrations",
|
||||
"invoice" => "Invoice",
|
||||
"invoice_configuration" => "Invoice Print Settings",
|
||||
"invoice_default_comments" => "Default Invoice Comments",
|
||||
"invoice_email_message" => "Invoice Email Template",
|
||||
"invoice_enable" => "Enable Invoicing",
|
||||
"invoice_printer" => "Invoice Printer",
|
||||
"invoice_type" => "Invoice Type",
|
||||
"is_readable" => "is readable, but the permissions are incorrectly set. Please set it to 640 or 660 and refresh.",
|
||||
"is_writable" => "is writable, but the permissions are incorrectly set. Please set it to 750 and refresh.",
|
||||
"item_markup" => "",
|
||||
"jsprintsetup_required" => "Warning: This functionality will only work if you have the FireFox jsPrintSetup addon installed. Save anyway?",
|
||||
"language" => "Language",
|
||||
"last_used_invoice_number" => "Last used Invoice Number",
|
||||
"last_used_quote_number" => "Last used Quote Number",
|
||||
"last_used_work_order_number" => "Last used W/O Number",
|
||||
"left" => "Left",
|
||||
"license" => "License",
|
||||
"license_configuration" => "License Statement",
|
||||
"line_sequence" => "Line Sequence",
|
||||
"lines_per_page" => "Lines per Page",
|
||||
"lines_per_page_number" => "Lines per Page must be a number.",
|
||||
"lines_per_page_required" => "Lines per Page is a required field.",
|
||||
"locale" => "Localization",
|
||||
"locale_configuration" => "Localization Configuration",
|
||||
"locale_info" => "Localization Configuration Information",
|
||||
"location" => "Stock",
|
||||
"location_configuration" => "Stock Locations",
|
||||
"location_info" => "Location Configuration Information",
|
||||
"login_form" => "Login Form Style",
|
||||
"logout" => "Do you want to make a backup before logging out? Click [OK] to backup or [Cancel] to logout.",
|
||||
"mailchimp" => "MailChimp",
|
||||
"mailchimp_api_key" => "MailChimp API Key",
|
||||
"mailchimp_configuration" => "MailChimp Configuration",
|
||||
"mailchimp_key_successfully" => "API Key is valid.",
|
||||
"mailchimp_key_unsuccessfully" => "API Key is invalid.",
|
||||
"mailchimp_lists" => "MailChimp List(s)",
|
||||
"mailchimp_tooltip" => "Click the icon for an API Key.",
|
||||
"message" => "Message",
|
||||
"message_configuration" => "Message Configuration",
|
||||
"msg_msg" => "Saved Text Message",
|
||||
"msg_msg_placeholder" => "If you wish to use a SMS template save your message here, otherwise leave the box blank.",
|
||||
"msg_pwd" => "SMS-API Password",
|
||||
"msg_pwd_required" => "SMS-API Password is a required field",
|
||||
"msg_src" => "SMS-API Sender ID",
|
||||
"msg_src_required" => "SMS-API Sender ID is a required field",
|
||||
"msg_uid" => "SMS-API Username",
|
||||
"msg_uid_required" => "SMS-API Username is a required field",
|
||||
"multi_pack_enabled" => "Multiple Packages per Item",
|
||||
"no_risk" => "No security/vulnerability risks.",
|
||||
"none" => "none",
|
||||
"notify_alignment" => "Notification Popup Position",
|
||||
"number_format" => "Number Format",
|
||||
"number_locale" => "Localization",
|
||||
"number_locale_invalid" => "The entered locale is invalid. Check the link in the tooltip to find a valid locale.",
|
||||
"number_locale_required" => "Number Locale is a required field.",
|
||||
"number_locale_tooltip" => "Find a suitable locale through this link.",
|
||||
"os_timezone" => "OSPOS Timezone:",
|
||||
"ospos_info" => "OSPOS Installation Info",
|
||||
"payment_options_order" => "Payment Options Order",
|
||||
"perm_risk" => "Incorrect permissions leaves this software at risk.",
|
||||
"phone" => "Company Phone",
|
||||
"phone_required" => "Company Phone is a required field.",
|
||||
"print_bottom_margin" => "Margin Bottom",
|
||||
"print_bottom_margin_number" => "Margin Bottom must be a number.",
|
||||
"print_bottom_margin_required" => "Margin Bottom is a required field.",
|
||||
"print_delay_autoreturn" => "Autoreturn to Sale delay",
|
||||
"print_delay_autoreturn_number" => "Autoreturn to Sale delay is a required field.",
|
||||
"print_delay_autoreturn_required" => "Autoreturn to Sale delay must be a number.",
|
||||
"print_footer" => "Print Browser Footer",
|
||||
"print_header" => "Print Browser Header",
|
||||
"print_left_margin" => "Margin Left",
|
||||
"print_left_margin_number" => "Margin Left must be a number.",
|
||||
"print_left_margin_required" => "Margin Left is a required field.",
|
||||
"print_receipt_check_behaviour" => "Print Receipt checkbox",
|
||||
"print_receipt_check_behaviour_always" => "Always checked",
|
||||
"print_receipt_check_behaviour_last" => "Remember last selection",
|
||||
"print_receipt_check_behaviour_never" => "Always unchecked",
|
||||
"print_right_margin" => "Margin Right",
|
||||
"print_right_margin_number" => "Margin Right must be a number.",
|
||||
"print_right_margin_required" => "Margin Right is a required field.",
|
||||
"print_silently" => "Show Print Dialog",
|
||||
"print_top_margin" => "Margin Top",
|
||||
"print_top_margin_number" => "Margin Top must be a number.",
|
||||
"print_top_margin_required" => "Margin Top is a required field.",
|
||||
"quantity_decimals" => "Quantity Decimals",
|
||||
"quick_cash_enable" => "",
|
||||
"quote_default_comments" => "Default Quote Comments",
|
||||
"receipt" => "Receipt",
|
||||
"receipt_category" => "",
|
||||
"receipt_configuration" => "Receipt Print Settings",
|
||||
"receipt_default" => "Default",
|
||||
"receipt_font_size" => "Font Size",
|
||||
"receipt_font_size_number" => "Font Size must be a number.",
|
||||
"receipt_font_size_required" => "Font Size is a required field.",
|
||||
"receipt_info" => "Receipt Configuration Information",
|
||||
"receipt_printer" => "Ticket Printer",
|
||||
"receipt_short" => "Short",
|
||||
"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",
|
||||
"report_an_issue" => "Report an issue",
|
||||
"return_policy_required" => "Return policy is a required field.",
|
||||
"reward" => "Reward",
|
||||
"reward_configuration" => "Reward Configuration",
|
||||
"right" => "Right",
|
||||
"sales_invoice_format" => "Sales Invoice Format",
|
||||
"sales_quote_format" => "Sales Quote Format",
|
||||
"mailpath_invalid" => "Invalid sendmail path. Only letters, numbers, dashes, underscores, slashes and dots are allowed.",
|
||||
"saved_successfully" => "Configuration save successful.",
|
||||
"saved_unsuccessfully" => "Configuration save failed.",
|
||||
"security_issue" => "Security Vulnerability Warning",
|
||||
"recv_invoice_format" => "Receivings Invoice Format",
|
||||
"register_mode_default" => "Default Register Mode",
|
||||
"report_an_issue" => "Report an issue",
|
||||
"return_policy_required" => "Return policy is a required field.",
|
||||
"reward" => "Reward",
|
||||
"reward_configuration" => "Reward Configuration",
|
||||
"right" => "Right",
|
||||
"sales_invoice_format" => "Sales Invoice Format",
|
||||
"sales_quote_format" => "Sales Quote Format",
|
||||
"mailpath_invalid" => "Invalid sendmail path. Only letters, numbers, dashes, underscores, slashes and dots are allowed.",
|
||||
"saved_successfully" => "Configuration save successful.",
|
||||
"saved_unsuccessfully" => "Configuration save failed.",
|
||||
"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",
|
||||
"statistics_tooltip" => "Send statistics for development and feature improvement purposes.",
|
||||
"stock_location" => "Stock location",
|
||||
"stock_location_duplicate" => "Stock Location must be unique.",
|
||||
"stock_location_invalid_chars" => "Stock Location can not contain '_'.",
|
||||
"stock_location_required" => "Stock location is a required field.",
|
||||
"suggestions_fifth_column" => "",
|
||||
"suggestions_first_column" => "Column 1",
|
||||
"suggestions_fourth_column" => "",
|
||||
"suggestions_layout" => "Search Suggestions Layout",
|
||||
"suggestions_second_column" => "Column 2",
|
||||
"suggestions_third_column" => "Column 3",
|
||||
"shortcuts" => "Shortcuts",
|
||||
"shortcuts_configuration" => "Sales Keyboard Shortcut Configuration",
|
||||
"shortcuts_duplicate_bindings" => "Shortcut bindings must be unique.",
|
||||
"shortcuts_save_error" => "Unable to save shortcut settings.",
|
||||
"system_conf" => "Setup & Conf",
|
||||
"system_info" => "System Info",
|
||||
"table" => "Table",
|
||||
"table_configuration" => "Table Configuration",
|
||||
"takings_printer" => "Receipt Printer",
|
||||
"tax" => "Tax",
|
||||
"tax_category" => "Tax Category",
|
||||
"tax_category_duplicate" => "The entered tax category already exists.",
|
||||
"tax_category_invalid_chars" => "The entered tax category is invalid.",
|
||||
"tax_category_required" => "The tax category is required.",
|
||||
"tax_category_used" => "Tax category cannot be deleted because it is being used.",
|
||||
"tax_configuration" => "Tax Configuration",
|
||||
"tax_decimals" => "Tax Decimals",
|
||||
"tax_id" => "Tax Id",
|
||||
"tax_included" => "Tax Included",
|
||||
"theme" => "Theme",
|
||||
"theme_preview" => "Preview Theme:",
|
||||
"thousands_separator" => "Thousands Separator",
|
||||
"timezone" => "Timezone",
|
||||
"timezone_error" => "OSPOS Timezone is Different from your Local Timezone.",
|
||||
"top" => "Top",
|
||||
"use_destination_based_tax" => "Use Destination Based Tax",
|
||||
"user_timezone" => "Local Timezone:",
|
||||
"website" => "Website",
|
||||
"wholesale_markup" => "",
|
||||
"work_order_enable" => "Work Order Support",
|
||||
"work_order_format" => "Work Order Format",
|
||||
];
|
||||
"show_office_group" => "Show office icon",
|
||||
"statistics" => "Send Statistics",
|
||||
"statistics_tooltip" => "Send statistics for development and feature improvement purposes.",
|
||||
"stock_location" => "Stock location",
|
||||
"stock_location_duplicate" => "Stock Location must be unique.",
|
||||
"stock_location_invalid_chars" => "Stock Location can not contain '_'.",
|
||||
"stock_location_required" => "Stock location is a required field.",
|
||||
"suggestions_fifth_column" => "",
|
||||
"suggestions_first_column" => "Column 1",
|
||||
"suggestions_fourth_column" => "",
|
||||
"suggestions_layout" => "Search Suggestions Layout",
|
||||
"suggestions_second_column" => "Column 2",
|
||||
"suggestions_third_column" => "Column 3",
|
||||
"system_conf" => "Setup & Conf",
|
||||
"system_info" => "System Info",
|
||||
"table" => "Table",
|
||||
"table_configuration" => "Table Configuration",
|
||||
"takings_printer" => "Receipt Printer",
|
||||
"tax" => "Tax",
|
||||
"tax_category" => "Tax Category",
|
||||
"tax_category_duplicate" => "The entered tax category already exists.",
|
||||
"tax_category_invalid_chars" => "The entered tax category is invalid.",
|
||||
"tax_category_required" => "The tax category is required.",
|
||||
"tax_category_used" => "Tax category cannot be deleted because it is being used.",
|
||||
"tax_configuration" => "Tax Configuration",
|
||||
"tax_decimals" => "Tax Decimals",
|
||||
"tax_id" => "Tax Id",
|
||||
"tax_included" => "Tax Included",
|
||||
"theme" => "Theme",
|
||||
"theme_preview" => "Preview Theme:",
|
||||
"thousands_separator" => "Thousands Separator",
|
||||
"timezone" => "Timezone",
|
||||
"timezone_error" => "OSPOS Timezone is Different from your Local Timezone.",
|
||||
"top" => "Top",
|
||||
"use_destination_based_tax" => "Use Destination Based Tax",
|
||||
"user_timezone" => "Local Timezone:",
|
||||
"website" => "Website",
|
||||
"wholesale_markup" => "",
|
||||
"work_order_enable" => "Work Order Support",
|
||||
"work_order_format" => "Work Order Format",
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ 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",
|
||||
"bank_transfer" => "Bank Transfer",
|
||||
"cancel_sale" => "Cancel",
|
||||
"cash" => "Cash",
|
||||
"cash_1" => "",
|
||||
@@ -20,6 +20,8 @@ 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",
|
||||
@@ -41,6 +43,7 @@ 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)",
|
||||
@@ -74,12 +77,6 @@ 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",
|
||||
@@ -110,6 +107,7 @@ 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",
|
||||
@@ -151,7 +149,9 @@ 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,10 +167,13 @@ 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",
|
||||
@@ -182,6 +185,7 @@ 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",
|
||||
@@ -212,6 +216,7 @@ 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",
|
||||
@@ -224,7 +229,6 @@ return [
|
||||
"update" => "Update",
|
||||
"upi" => "UPI",
|
||||
"visa" => "",
|
||||
"wallet" => "Wallet",
|
||||
"wholesale" => "",
|
||||
"work_order" => "Work Order",
|
||||
"work_order_number" => "Work Order Number",
|
||||
@@ -232,3 +236,5 @@ return [
|
||||
"work_order_sent" => "Work Order sent to",
|
||||
"work_order_unsent" => "Work Order failed to be sent to",
|
||||
];
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
249
app/Views/partial/customer_display_header.php
Normal file
249
app/Views/partial/customer_display_header.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?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">
|
||||
|
||||
224
app/Views/sales/customer_display.php
Normal file
224
app/Views/sales/customer_display.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* @var array $cart
|
||||
* @var array $config
|
||||
* @var float $rate
|
||||
* @var float $total
|
||||
* @var float $subtotal
|
||||
* @var float $prediscount_subtotal
|
||||
* @var array $taxes
|
||||
* @var array $payments
|
||||
* @var float $amount_change
|
||||
*/
|
||||
|
||||
$priceWithCurrencyLabel = lang('Sales.price_with_currency');
|
||||
|
||||
?>
|
||||
|
||||
<?= view('partial/customer_display_header') ?>
|
||||
|
||||
<div class="customer-display-cart-column">
|
||||
<div class="register-wrap">
|
||||
<div class="panel panel-default customer-display-items-panel">
|
||||
<div class="panel-heading"><?= lang('Sales.items') ?></div>
|
||||
<div class="panel-body table-responsive">
|
||||
<table class="table table-striped table-condensed" id="register">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: <?= (int) $cartItemWidth ?>%;"><?= lang('Sales.item_name') ?></th>
|
||||
<?php if ($cartHasCustomerDisplay) { ?>
|
||||
<th style="width: <?= (int) $cartPriceWidth ?>%;"><?= sprintf($priceWithCurrencyLabel, esc($customerDisplayCurrencyLabel)) ?></th>
|
||||
<?php } ?>
|
||||
<th style="width: <?= (int) $cartOriginalWidth ?>%;"><?= sprintf($priceWithCurrencyLabel, esc($originalCurrencyLabel)) ?></th>
|
||||
<th style="width: <?= (int) $cartQuantityWidth ?>%;"><?= lang('Sales.quantity') ?></th>
|
||||
<th style="width: <?= (int) $cartDiscountWidth ?>%;"><?= lang('Sales.discount') ?></th>
|
||||
<th style="width: <?= (int) $cartTotalWidth ?>%;"><?= lang('Sales.total') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cart_contents">
|
||||
<?php if (count($cart) == 0) { ?>
|
||||
<tr>
|
||||
<td colspan="<?= (int) $cartColspan ?>">
|
||||
<div class="alert alert-dismissible alert-info"><?= lang('Sales.no_items_in_cart') ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else { ?>
|
||||
<?php foreach (array_reverse($cart, true) as $line => $item) { ?>
|
||||
<tr>
|
||||
<td class="item-name-cell">
|
||||
<?= esc($item['name']) ?><br>
|
||||
<?= !empty($item['attribute_values']) ? esc($item['attribute_values']) : '' ?>
|
||||
</td>
|
||||
<?php if ($cartHasCustomerDisplay) { ?>
|
||||
<td class="price-cell">
|
||||
<?= to_secondary_currency((float)$item['price'], $secondaryCurrency) ?>
|
||||
</td>
|
||||
<?php } ?>
|
||||
<td class="price-cell">
|
||||
<?= to_currency($item['price']) ?>
|
||||
</td>
|
||||
<td class="price-cell">
|
||||
<?= to_quantity_decimals($item['quantity']) ?>
|
||||
</td>
|
||||
<td class="price-cell">
|
||||
<?= to_decimals($item['discount'], 0) ?>
|
||||
</td>
|
||||
<td class="price-cell">
|
||||
<?= $item['item_type'] == ITEM_AMOUNT_ENTRY ? to_currency_no_money($item['discounted_total']) : to_currency($item['discounted_total']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="<?= $cartHasCustomerDisplay ? 3 : 2 ?>"></td>
|
||||
<td class="serial-cell">
|
||||
<?= $item['is_serialized'] == 1 ? lang('Sales.serial') : '' ?>
|
||||
</td>
|
||||
<td colspan="2" class="serial-cell">
|
||||
<?php if ($item['is_serialized'] == 1) {
|
||||
echo esc($item['serialnumber']);
|
||||
} ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customer-display-summary-column">
|
||||
<div class="panel panel-primary customer-display-summary-panel">
|
||||
<div class="panel-heading"><?= lang('Sales.summary') ?></div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed summary-subtable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><?= lang('Sales.total') ?></th>
|
||||
<td><?= to_currency($total) ?></td>
|
||||
</tr>
|
||||
<?php if ($showCustomerDisplay): ?>
|
||||
<tr>
|
||||
<th><?= lang('Sales.total') ?> <?= esc($customerDisplayCurrencyLabel) ?></th>
|
||||
<td><?= to_secondary_currency((float)$total, $secondaryCurrency) ?></td>
|
||||
</tr>
|
||||
<tr class="rate-row">
|
||||
<th><?= lang('Sales.rate') ?></th>
|
||||
<td><?= number_format((float) $rate, 2) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-condensed summary-subtable" style="margin-top: 10px;">
|
||||
<tbody>
|
||||
<tr class="summary-section-row">
|
||||
<th colspan="2"><?= lang('Sales.customer') ?></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= lang('Sales.customer_name') ?></th>
|
||||
<td class="customer-name-value"><?= esc($customerName ?? lang('Sales.walk_in_customer')) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= lang('Sales.giftcard_balance') ?></th>
|
||||
<td class="giftcard-value"><?= to_currency((float) ($giftcardRemainder ?? 0)) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= lang('Sales.loyalty_reward_points') ?></th>
|
||||
<td class="reward-value"><?= esc((string)($customerRewardPoints ?? 0)) ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-condensed summary-subtable" style="margin-top: 10px;">
|
||||
<tbody>
|
||||
<tr class="summary-section-row">
|
||||
<th colspan="2"><?= lang('Sales.change') ?></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= lang('Sales.payments_total') ?></th>
|
||||
<td><?= to_currency($payments_total) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= lang('Sales.amount_due') ?></th>
|
||||
<td><?= to_currency($amount_due) ?></td>
|
||||
</tr>
|
||||
<?php if ($showCustomerDisplay): ?>
|
||||
<tr>
|
||||
<th><?= lang('Sales.amount_due') ?> <?= esc($customerDisplayCurrencyLabel) ?></th>
|
||||
<td><?= to_secondary_currency((float)$amount_due, $secondaryCurrency) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<th><?= lang('Sales.change_due') ?></th>
|
||||
<td><?= to_currency($paymentChangeDue ?? 0) ?></td>
|
||||
</tr>
|
||||
<?php if ($showCustomerDisplay): ?>
|
||||
<tr>
|
||||
<th><?= lang('Sales.change_due') ?> <?= esc($customerDisplayCurrencyLabel) ?></th>
|
||||
<td><?= to_secondary_currency((float)($paymentChangeDue ?? 0), $secondaryCurrency) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="customer-display-footer"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const customerDisplayId = new URLSearchParams(window.location.search).get('displayId') || '';
|
||||
const customerDisplayStorageSuffix = customerDisplayId !== '' ? '_' + customerDisplayId : '';
|
||||
const customerDisplayStorageKeys = {
|
||||
open: 'customerDisplayOpen' + customerDisplayStorageSuffix,
|
||||
dirtyAt: 'customerDisplayDirtyAt' + customerDisplayStorageSuffix
|
||||
};
|
||||
|
||||
localStorage.setItem(customerDisplayStorageKeys.open, '1');
|
||||
|
||||
let lastDirtyAt = localStorage.getItem(customerDisplayStorageKeys.dirtyAt) || '';
|
||||
let refreshTimer = null;
|
||||
|
||||
const scheduleRefresh = function(dirtyAt) {
|
||||
if (refreshTimer !== null) {
|
||||
clearTimeout(refreshTimer);
|
||||
}
|
||||
|
||||
refreshTimer = setTimeout(function() {
|
||||
if (localStorage.getItem(customerDisplayStorageKeys.open) !== '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localStorage.getItem(customerDisplayStorageKeys.dirtyAt) === dirtyAt) {
|
||||
window.location.reload();
|
||||
}
|
||||
}, 700);
|
||||
};
|
||||
|
||||
const checkForRefresh = function() {
|
||||
const dirtyAt = localStorage.getItem(customerDisplayStorageKeys.dirtyAt) || '';
|
||||
if (dirtyAt !== '' && dirtyAt !== lastDirtyAt) {
|
||||
lastDirtyAt = dirtyAt;
|
||||
scheduleRefresh(dirtyAt);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', function(event) {
|
||||
if (event.key === customerDisplayStorageKeys.dirtyAt) {
|
||||
checkForRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
setInterval(checkForRefresh, 500);
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
localStorage.removeItem(customerDisplayStorageKeys.open);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user