[
  {
    "path": ".github/workflows/ci.yml",
    "content": "\nname: CI\n\non:\n  push:\n    branches:\n      - develop\n  pull_request:\n\nconcurrency:\n  group: develop-ury-${{ github.event.number }}\n  cancel-in-progress: true\n\njobs:\n  tests:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n    name: Server\n\n    services:\n      mariadb:\n        image: mariadb:10.6\n        env:\n          MYSQL_ROOT_PASSWORD: root\n        ports:\n          - 3306:3306\n        options: --health-cmd=\"mysqladmin ping\" --health-interval=5s --health-timeout=2s --health-retries=3\n\n    steps:\n      - name: Clone\n        uses: actions/checkout@v2\n\n      - name: Setup Python\n        uses: actions/setup-python@v2\n        with:\n          python-version: '3.10'\n\n      - name: Setup Node\n        uses: actions/setup-node@v2\n        with:\n          node-version: 14\n          check-latest: true\n\n      - name: Cache pip\n        uses: actions/cache@v2\n        with:\n          path: ~/.cache/pip\n          key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }}\n          restore-keys: |\n            ${{ runner.os }}-pip-\n            ${{ runner.os }}-\n\n      - name: Get yarn cache directory path\n        id: yarn-cache-dir-path\n        run: 'echo \"::set-output name=dir::$(yarn cache dir)\"'\n\n      - uses: actions/cache@v2\n        id: yarn-cache\n        with:\n          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}\n          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-yarn-\n\n      - name: Setup\n        run: |\n          pip install frappe-bench\n          bench init --skip-redis-config-generation --skip-assets --python \"$(which python)\" ~/frappe-bench\n          mysql --host 127.0.0.1 --port 3306 -u root -proot -e \"SET GLOBAL character_set_server = 'utf8mb4'\"\n          mysql --host 127.0.0.1 --port 3306 -u root -proot -e \"SET GLOBAL collation_server = 'utf8mb4_unicode_ci'\"\n\n      - name: Install\n        working-directory: /home/runner/frappe-bench\n        run: |\n          bench get-app ury $GITHUB_WORKSPACE\n          bench setup requirements --dev\n          bench new-site --db-root-password root --admin-password admin test_site\n          bench --site test_site install-app ury\n          bench build\n        env:\n          CI: 'Yes'\n\n      - name: Run Tests\n        working-directory: /home/runner/frappe-bench\n        run: |\n          bench --site test_site set-config allow_tests true\n          bench --site test_site run-tests --app ury\n        env:\n          TYPE: server\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n*.pyc\n*.egg-info\n*.swp\ntags\nury/docs/current\nnode_modules/\n\nury/public/pos\nury/www/pos.html\nury/public/urypos\nury/public/URYMosaic\nury/public/node_modules\nurypos/yarn.lock\nURYMosaic/yarn.lock\nury/www/urypos.html\nury/www/URYMosaic.html\nbuild\n\n*.lock\n"
  },
  {
    "path": "AGENTS.MD",
    "content": "# URY — Root Agent Documentation\n\n## 1. App Overview\n\n**URY** is a complete restaurant order management system built as a Frappe/ERPNext custom app.\n\n- **Publisher:** Tridz Technologies Pvt. Ltd\n- **Requires:** ERPNext (must be installed in the Frappe bench)\n- **Version:** 0.2.1\n- **License:** MIT\n\nThe system covers the full restaurant workflow: menu management, table management, order taking (POS), kitchen display (KOT), payments (integrated with ERPNext POS), P&L reporting, and multi-branch support.\n\n**POS frontend is located in `/pos` and has its own `AGENTS.MD` with detailed documentation.**\n\n---\n\n## 2. Repository Structure\n\n```\nury/                          ← repo root\n├── ury/                      ← Frappe app Python package (main backend)\n├── pos/                      ← React 19 POS frontend (v2, current)\n├── URYMosaic/                ← Vue 3 KOT kitchen display app\n├── urypos/                   ← Vue 3 POS frontend (v1, legacy)\n├── DEMO/                     ← Demo screenshots / assets\n├── requirements.txt          ← Python dependencies\n├── setup.py                  ← Python package setup\n├── package.json              ← Yarn workspace root (manages all JS apps)\n└── FEATURES.md / README.md   ← Human-readable documentation\n```\n\nThe Yarn workspace (`package.json`) at the root manages three JS sub-projects: `pos/`, `URYMosaic/`, and `urypos/`.\n\n---\n\n## 3. Frappe Backend Structure (`ury/`)\n\n```\nury/\n├── hooks.py              ← App registration, doc_events, scheduler, fixtures\n├── patches.txt           ← Migration patch list\n├── setup.py              ← Custom field creation (add_custom_fields)\n├── uninstall.py          ← Cleanup on app removal\n├── install.py            ← Post-install setup\n├── permission.py         ← check_app_permission for app screen\n│\n├── ury_pos/\n│   └── api.py            ← Primary REST API for POS frontend (722 lines)\n│                           Whitelisted methods: getRestaurantMenu, getBranch,\n│                           getModeOfPayment, getPosProfile, getAggregatorItem,\n│                           createPaymentEntry, getInvoiceForCashier, etc.\n│\n├── ury/\n│   ├── doctype/          ← 35 custom Frappe doctypes (see §4)\n│   ├── hooks/            ← Document event handlers\n│   │   ├── ury_pos_invoice.py        ← before_insert, validate, before_submit, on_cancel\n│   │   ├── ury_pos_profile.py        ← validate\n│   │   ├── ury_sales_invoice.py      ← before_insert, on_update\n│   │   ├── ury_item.py               ← validate\n│   │   ├── ury_pos_opening_entry.py  ← set_cashier_room, before_save\n│   │   └── ury_pos_closing_entry.py  ← before_save, validate\n│   ├── api/              ← Modular API handlers\n│   │   ├── ury_kot_display.py        ← KOT list, serve/confirm KOT\n│   │   ├── ury_kot_order_number.py   ← Daily order number logic\n│   │   ├── ury_kot_validation.py     ← Scheduled KOT validation (every minute)\n│   │   ├── ury_kot_notification.py   ← Order delay notifications\n│   │   └── ury_menu_course_validation.py ← Course priority validation\n│   └── page/\n│       └── websocket_print/          ← Real-time print via websocket\n│\n├── www/                  ← Web page Python context providers\n│   ├── pos.py            ← Boot context for /pos page\n│   └── *.html            ← Build output entry points (generated by Vite build)\n│\n├── public/               ← Static assets served by Frappe\n│   ├── js/               ← Client-side JS injected via hooks.py\n│   │   ├── quick_entry.js            ← POS quick entry customisation\n│   │   ├── pos_print.js              ← Standard POS print customisation\n│   │   ├── pos_extend.js             ← Injected on /point-of-sale page\n│   │   ├── restrict_qty_edit_pos.js  ← Prevents qty edit in legacy POS\n│   │   ├── ury_pos_kot.js            ← KOT related JS\n│   │   └── sign-message.js / jsrsasign-all-min.js  ← QZ Tray signing\n│   ├── pos/              ← Built React POS assets (output of `cd pos && yarn build`)\n│   ├── URYMosaic/        ← Built KOT display assets\n│   └── urypos/           ← Built legacy POS assets\n│\n├── fixtures/             ← Data fixtures exported via `bench export-fixtures`\n│   ├── custom_field.json         ← All custom fields definitions\n│   ├── role.json                 ← URY Roles (URY Admin, URY Cashier…)\n│   ├── property_setter.json      ← Field label overrides\n│   ├── custom_html_block.json\n│   └── client_script.json\n│\n├── templates/\n│   └── pages/            ← Jinja web page templates (currently minimal)\n│\n└── patches/\n    └── v2_0/\n        └── default_permissions.py  ← Post-migration permission setup\n```\n\n---\n\n## 4. Key Doctypes\n\n| Doctype | Purpose |\n|---|---|\n| `URY Order` | Core order document. Created/updated by POS. Linked to POS Invoice on payment. |\n| `URY Order Item` | Line items for URY Order. |\n| `URY KOT` | Kitchen Order Ticket generated when an order is placed/modified. |\n| `URY KOT Items` | Line items for a KOT. |\n| `URY Menu` | Menu definition linked to a Restaurant and Price List. |\n| `URY Menu Item` | Individual item in a menu with rate and image. |\n| `URY Menu Course` | Course grouping (starter, main, dessert) with serve priority. |\n| `URY Restaurant` | Restaurant master record. |\n| `URY Table` | Table in a restaurant room. |\n| `URY Room` | Section/room within a restaurant. |\n| `URY Printer Settings` | Thermal printer configuration (IP, port, format). |\n| `URY User` | Waiter/cashier assignment to a branch. |\n| `Aggregator Settings` | Delivery platform (Zomato, Swiggy) configuration. |\n| `Item Add On` | Modifier/add-on for a menu item. |\n| `POS Item Variants` | Size/variant options for a menu item. |\n| `URY Daily P and L` | Daily P&L report. |\n| `URY Cost of Goods` | COGS tracking doctype. |\n| `Sub POS Closing` | POS closing record per cashier. |\n\n**Custom fields added to standard ERPNext doctypes** (via `setup.py` + `fixtures/custom_field.json`):\n- `POS Invoice` / `Sales Invoice`: `order_type`, `waiter`, `no_of_pax`, `cashier`, `restaurant`, `branch`, `restaurant_table`, `invoice_printed`, `cancel_reason`, `custom_comments`, `custom_ury_order_number`\n- `POS Profile`: `restaurant`, `branch`, `printer_settings`, `qz_print`, `qz_host`, `enable_discount`, `enable_multiple_cashier`, `reset_order_number_daily`\n- `POS Opening Entry`: `restaurant`, `branch`, `custom_room`, `custom_rooms`\n- `Branch`: `user` (URY User table), `custom_aggregators`\n- `Customer`: `mobile_number`\n- `Price List`: `restaurant_menu`\n\n---\n\n## 5. Document Event Hooks (`hooks.py`)\n\n| DocType | Event | Handler |\n|---|---|---|\n| `POS Invoice` | `before_insert` | Set arrived_time, validate restaurant/branch |\n| `POS Invoice` | `validate` | Validate order fields |\n| `POS Invoice` | `after_insert` | Set daily order number |\n| `POS Invoice` | `before_submit` | Final validation before submit |\n| `POS Invoice` | `on_cancel` / `on_trash` | Cleanup KOTs |\n| `POS Profile` | `validate` | Validate printer/restaurant setup |\n| `Sales Invoice` | `before_insert`, `on_update` | Copy restaurant fields from linked POS Invoice |\n| `Item` | `validate` | Validate menu item configuration |\n| `POS Opening Entry` | `validate` | Set cashier room assignment |\n| `POS Opening Entry` | `before_save` | Validation before save |\n| `POS Opening Entry` | `before_insert` | Set last invoice reference |\n| `POS Closing Entry` | `before_save`, `validate` | Closing validation |\n\n**Scheduler:** `ury.ury.api.ury_kot_validation.kotValidationThread` runs every minute to validate KOT state.\n\n---\n\n## 6. Integration Points\n\n### ERPNext Integration\n- URY orders ultimately create **POS Invoices** (and **Sales Invoices** on consolidation) in ERPNext.\n- Payment is processed via `make_invoice` which calls ERPNext's POS payment flow.\n- Price Lists, Customers, Payment Modes, and Tax Templates are all standard ERPNext objects.\n\n### Frontend Apps\n- **`/pos`** — React POS v2 (see `pos/AGENTS.MD`)\n- **`/urypos`** — Vue POS v1 (legacy, `urypos/`)\n- **`/URYMosaic/<production_unit>`** — Vue KOT display (see `URYMosaic/AGENTS.MD`)\n\nAll frontends are served as Frappe web pages. Route rules in `hooks.py` (`website_route_rules`) handle SPA routing:\n```python\n{\"from_route\": \"/pos/<path:app_path>\", \"to_route\": \"pos\"},\n{\"from_route\": \"/URYMosaic/<path:app_path>\", \"to_route\": \"URYMosaic\"},\n```\n\n### Real-time (Socket.io)\n- KOT updates are broadcast via Frappe's Socket.io to the `URYMosaic` kitchen display.\n- Channel format: `kot_update_{branch}_{production_unit}`\n\n### QZ Tray\n- Thermal printing uses QZ Tray (desktop app). The POS sends signed print jobs.\n- Signing keys: `public/js/sign-message.js` + `jsrsasign-all-min.js`\n- QZ host is configured per POS Profile (`qz_host` field).\n\n---\n\n## 7. Where Frontend Lives\n\n| App | Source | Build output | URL |\n|---|---|---|---|\n| POS v2 (React) | `pos/src/` | `ury/public/pos/` | `/pos` |\n| KOT display (Vue) | `URYMosaic/src/` | `ury/public/URYMosaic/` | `/URYMosaic/<unit>` |\n| POS v1 (Vue, legacy) | `urypos/src/` | `ury/public/urypos/` | `/urypos` |\n\nBuild commands (from repo root):\n```bash\ncd pos && yarn build       # builds React POS\ncd URYMosaic && yarn build # builds KOT display\ncd urypos && yarn build    # builds legacy POS\n```\n\nOr from root: `yarn build` (if configured in root package.json).\n\nAfter building, run `bench build --app ury` to copy assets to the Frappe public directory.\n\n---\n\n## 8. How Agents Should Navigate This Repo\n\n**To modify POS UI behaviour or translations:**\n→ Work in `pos/`. Read `pos/AGENTS.MD` first.\n\n**To modify KOT display logic:**\n→ Work in `URYMosaic/`. Read `URYMosaic/AGENTS.MD` first.\n\n**To add or modify a Frappe API endpoint:**\n→ Edit `ury/ury_pos/api.py` (main POS API) or `ury/ury/api/<module>.py`.\n→ Decorate with `@frappe.whitelist()`. No router config needed.\n\n**To add a new doctype:**\n→ Use `bench new-doctype` or create JSON in `ury/ury/doctype/<name>/`.\n→ Export fixtures after changes: `bench export-fixtures --app ury`.\n\n**To add a custom field to a standard doctype:**\n→ Add via Frappe desk, then export with `bench export-fixtures --app ury`.\n→ OR add programmatically in `ury/setup.py` `add_custom_fields()`.\n\n**To add a migration patch:**\n→ Create file in `ury/patches/v<major>_<minor>/`.\n→ Add path to `ury/patches.txt` under `[post_model_sync]`.\n\n**To change document event behaviour:**\n→ Edit the relevant file in `ury/ury/hooks/`.\n→ Hook registration is in `hooks.py` `doc_events`.\n\n**Never:**\n- Modify `ury/public/pos/`, `ury/public/URYMosaic/`, or `ury/public/urypos/` directly — these are build output, not source.\n- Edit `fixtures/custom_field.json` directly — export from Frappe desk instead.\n- Add business logic to `www/pos.py` — it should only return boot context data.\n"
  },
  {
    "path": "CLAUDE.MD",
    "content": "AGENTS.MD\n"
  },
  {
    "path": "FEATURES.md",
    "content": "# Features of URY App\n\nIt's important to note that if no POS Opening entry is created for the day, URY will not allow table selection, ensuring accurate tracking of operations.\nA POS Closing Entry must be created at the end of each day to complete the daily operations.\n\n> :information_source: **Note**  \n> This version is currently designed for **POS machines/Desktop** to handle **cashiers and fast checkout**.  \n> For **order takers and mobile support**, use **Version 1 POS**, which is available at the path `/urypos/Table`.\n\n- **Key Features**\n\t- All Major POS Features from Version 1 Retained\n\t- Core functionalities from the previous version are preserved for consistency.\n\n- **Unified Order-Taking Interface**\n\t- A single page handles the entire order flow—streamlining operations and reducing clicks.\n\n- **Dynamic Header Search Bar**\n\t- In POS Page: Search for menu items.\n\t- In Order Page: Search by customer name, invoice ID, etc.\n\n- **Table Selection for Dine-In Orders**\n\t- Tables are displayed using shapes and colors.\n\t- Shapes are configurable via the URY Table Doctype.\n\n- **Menu Item Interactions**\n\t- Double Click: Opens detailed product page for customizations.\n\t- Single Click: Instantly adds item to cart.\n\n- **Sidebar Menu Course Navigation**\n\t- A left sidebar on the POS page allows quick switching between menu courses (e.g., Starters, Mains, Desserts).\n\n### Version 1\n\n- **Room Selection**\n\t\n\t-  To view tables in each room of the restaurant and select their preferred room.\n\n- **Table Selection**\n\n\t- URY POS table order taking workflow begins with table selection.\n\t- Tables are visually represented as cards, providing flexibility in the selection process\n\t- On the top left side of table have badges that indicated table status:\n\t\t- Attention(Red): Indicates the table occupied for more than Table Attention time in POS Profile.\n\t\t- Occupied(Yellow): Indicates occupied tables.\n\t\t- Free(Green):Signifies available tables\n\t\t- Active(Blue): Highlights the currently selected table.\n\t- Occupied table propeties\n\t\t- On the top right of table has button that contains a drop down for table and captain transfer\n\t\t\t- Table Transfer : Transfer an order from one table to another after placing the initial order. Clear the original table, and occupy the new table.\n\t\t\t- Captain Transfer : Enables the transfer of an order from one captain to another after placing an order at a table.\n\t\t- `Bill` button to generate a bill against the order, clearing the table.\n\t\t- `Eye icon` button for navigate to the order page     \n\t\t- Table time is displayed within each card\n\t\t- On selecting a table with an existing order, will automatically navigate to the menu page.\n\n- **Menu Selection**\n\n\t- Search bar to quickly locate specific menu items, enhancing speed and accuracy during busy periods.\n\t- Toggle visibility of menu item image based on \"View Item Image\" checkbox in POS profile.\n\t- Menu filtering with a select box for selecting courses from the restaurant menu , which displays the available courses.\n\t- Menu filtering with Button All - Display all menu item. Priority - displays only prioritized items.\n\t- Menus are presented in a card format which includes the menu name, selected quantity of the item , and +/- buttons for adjusting quantities.\n\t- For precise quantity adjustments, users can click on the quantity display, triggering a dialogue box for easy modification and item wise comments.\n\n- **Customer Selection**\n\t\n\t- Option to create a customer using the customer's name and mobile number. You can also add the Customer Group and Territory if needed.\n\t- Option to search for an existing customer using the name or mobile number.\n\t- Option to enter number of pax.\n\t- For returning customers, URY POS displays their top three ordered items in Favourite item section.\n\n- **Cart**\n\n\t- Displays ordered items and their quantities.\n\t- Users can click on the quantity to open a dialog box for precise adjustments and add item-wise comments.\n\t- Includes a delete button to remove items from the cart.\n\t- Shows the grand total.\n\t- Option to add a general comment to the order.\n\t- Displays additional details such as invoice number, waiter name, POS profile, and cashier.\n\t- Action Buttons in Cart,\n\t\t- *Update* : Used to generate an order, ie. creating a POS invoice in draft status.\n\t\t- *Cancel* : To cancel order (draft invoice) and clear the table.\n\t\t- *KOT Reprint* : Used to reprint KOT.\n\n- **Takeaway Order Taking** \n\n\t- Takeaway orders can be placed by selecting the `Order Type` on the table page, choosing menu items, adding customer details, and clicking the Update button. This will redirect to the `OrderLog` page.\n\t- Option to search recent orders using invoice ID,customer name and mobile number.\n\t- The Order Log page displays recently placed invoices based on their status:\n\t\t- `Draft`: Shows takeaway orders and billed table orders.\n\t\t- `Unbilled`: Displays unbilled table orders.\n\t\t- `Recently Paid`: Displays a limited number of recently paid invoices, based on the limit set in the POS Profile.\n\t\t- `Paid`, `Consollidated` and `Return` : Show all paid, consolidated, and returned orders based on the selected order type. These statuses are visible only if the `Allow Cashier to View All Status` checkbox is enabled in the POS Profile.\n\t- `Edit`: Used to modify recent orders.\n\t- `Print Receipt`: To print the invoice.\n\t- `Make Payment`: Allows settling the invoice by selecting a mode of payment.\n\t- `Cancel Order`: To cancel order.\n\n- **Order Printing**\n\n\t- URY facilitates room-wise printing and offers three distinct methods for executing printing.\n\t\t- QZ printing\n\t\t\t- You may need first install [QZ Tray](https://qz.io/download/) if is not already on your system\n\t\t\t- To setup [QZ](https://qz.io/docs/print-server) , \n\t\t\t\tPOS Profile List > POS Profile > QZ Print > QZ Host to enable QZ printing.\n\t\t\t- If there are multiple devices for printing , Private IP of the machine hosting the QZ server is given as 'QZ Host'\n\t\t\t- Otherwise, use 'localhost' or 127.0.0.1 in the 'QZ Host' data field.\n\t\t- Network Printing\n\t\t\t- Network printing is an alternative option, which functions when QZ printing is in a disabled state.\n\t\t\t- To setup it \n\t\t\t\tPOS Profile List > POS Profile > Printer Settings\n\t\t\t- At the table, tsetting for the printer name is provided with the checkbox 'Bill' set to true.\n\t\t\t- The printer name correspond to printer configured in [Network Printer Settings](https://docs.erpnext.com/docs/user/manual/en/print-settings#3-network-printer-print-server) in ERPnext.\n\t\t- Websocket Printing\n\t\t\t- If Either of QZ and Network Printing are not configured , URY will call  websocket printing.\n\t\t\t- Page can be accessed in `/app/websocket-print` in your browser\n\n\n**MOSAIC (Kitchen Order Ticket)**\n\n- **KOT Generation:**\n\t- KOT are generated when order is placed in the system\n\t- Update button in order taking and checkout in POS will trigger initial KOT\n\n- **Modified KOT:**\n\t- Adding new item / quantity to the existing order will generate a modified KOT \n  \n- **Partially cancelled KOT:**\n\t- Removing an Item / reducing quantity from existing order  will generate a Partially cancelled KOT.\n \n- **Cancelled KOT:**\n\t- Created when an entire order is cancelled .\n  \n- **KOT Comments**\n\t- Can attach item-wise and order-level comments to KOTs, visible in the Kitchen Display System (KDS)\n  \n- **Production Units** \n\t- Production units are used to rule multiple kitchens.\n\t- Each production unit has its own dedicated web-based interface, displaying specific items.\n\t- Printers can be configured separately for each production unit.\n\t- KDS displays are organised by these units , access KDS via \n\t\t`/URYMosaic/<URY_Production_Unit>`   \n    \n- **KOT Display**\n\t- KDS make easy to monitor kitchen orders (KOTs) on a screen..\n\t- Receive real-time updates for new KOTs and table changes.\n\t- KOTs are displayed as cards with the following details ,\n\t\t- Order Type (Table or Takeaway).\n\t\t- Table name for table orders.\n\t\t- User who placed the order\n\t\t- POS Invoice ID as Order ID.\n\t\t- KOT Created Time \n\t\t- Item name , quantity and item wise comments.\n\t\t- Order-level comments.\n\t- Display available quantity and old quantity for canceled orders. \n\t- Ability to mark items as served and unserved.\n\t- Timer against KOT are set in \"KOT Warning Time\" field within the POS profile to trigger a warning when it's exceeded. \n\t- Can Enable \"Notify KOT Delay\" for KOT delay notification feature in the POS profile and add recipients roles to the Recipients table.\n\t- Can Enable 'KOT Audio Alert' to play a sound when a KOT is displayed. You can add an audio alert in the 'KOT alert sound' attachment field\n\t- For Cancelled order , card display available quantity and old quantity.\n\t- Clicking outside the items section on a KOT card reveals two buttons:\n\t\t- \"Serve\"  : Remove Card from the KDS and mark Serve time in KOT document.\n\t\t- \"Confirm\" (only for canceled KOTs): Confirms the cancellation..\n\t\t- Clicking on the card again returns to the original view.\n\t- KOTs are color-coded for easy identification:\n\t\t- White : New KOT for table orders\n\t\t- Blue : New KOT for takeaway orders\n\t\t- Orange : Modified KOT orders\n\t\t- Red : Cancelled or partially cancelled KOT orders\n\n- **KOT Print**\n\t- Generate physical copies of KOTs\n\t- KOT Prints are generated when order are placed\n\t- Configure printers through network printing in POS profiles for global printing \n\t- To print KOT in the production unit and room, you need to configure the printer separately for each location.\n\n**Daily Profit and Loss:**\n\n- **Daily Profit and Loss report. Here's how it works:**\n\n\t- Buying Price List :\n\t\t- Users can set the buying price list to calculate the cost of goods.\n\n\t- Direct Expenses :\n\t\t- This section includes expenses such as consumables (burning materials), their cost per unit, and direct fixed expenses (daily fixed expenses). You can set expense names and amounts for items that are part of your daily operational costs.\n  \n\t- Indirect Expenses :\n\t\t-  Indirect expenses consist of electricity costs per unit, expense names, and amounts that are also daily fixed expenses. Additionally, there's a section for percentage expenses, which allows you to store expense percentages based on either net sales or gross sales. You can also set depreciation in this section.\n\n- **Daily P and L Calculation:**\n\n\t***Once you've set up the necessary data in the Daily Profit and Loss section of URY Report Settings, the system can calculate your daily profit and loss, including the following components:***\n\n\t- Gross Sales: The total sales for the day..\n\t- Direct Expenses: The sum of consumables and other direct fixed expenses.\n\t- Cost of Goods Sold: This includes the cost of the items sold, factoring in product bundles and Bill of Materials (BOM) sub-items. \n\t- Gross Profit/Loss: The difference between gross sales and the cost of goods sold.\n\t- Employee Cost: The total Employee Cost for the day, including wages, salaries, benefits, and any other related expenses. This cost is part of the Total Indirect Expense.\n\t- Indirect Expenses: The total of indirect fixed expenses and any percentage-based expenses.\n\t- Net Profit/Loss: The final profit or loss for the day.\n \n\t***By inputting daily readings for consumables, electricity, and any other expenses, and then saving and submitting the document, you can generate a detailed daily profit and loss report, complete with a breakdown of the cost of goods sold. This report is an invaluable tool for restaurant owners and managers to track their financial performance on a daily basis.***\n\n- **Reports:** \n\n\t***It offers a wide range of reports, including the following:***\n\n\t1. Today's Sales\n\t2. Daywise Sales\n\t3. Daywise Invoices\n\t4. Month Wise Sales\n\t5. Average Bill Value\n\t6. Cancelled Invoices\n\t7. Item Wise Sales\n\t8. Customer Data\n\t9. Repeated Customers\n\t10. Daywise Customer Details\n\t11. Employee Sales\n\t12. Employee Item Wise Sales\n\t13. Service Wise Sales\n\t14. Time Wise Sales\n  \n- **Customizable Branch Timings:**\n\t- URY allows you to set varying branch timings in the URY Report settings, including extended hours. This feature ensures that your reports accurately reflect the operational hours of your restaurant.\n"
  },
  {
    "path": "INSTALLATION.md",
    "content": "# URY Installation\n\nWhile URY may work on existing ERPNext instance, it is recommended that you setup URY on a new  frappe site created for URY.\n\n<div align=\"center\">\n\t<a href=\"https://frappecloud.com/dashboard/signup?product=ury\" target=\"_blank\">\n\t\t<picture>\n\t\t\t<source media=\"(prefers-color-scheme: dark)\" srcset=\"https://frappe.io/files/try-on-fc-white.png\">\n\t\t\t<img src=\"https://frappe.io/files/try-on-fc-black.png\" alt=\"Try on Frappe Cloud\" height=\"28\" />\n\t\t</picture>\n\t</a>\n</div>\n\n\n\n> :information_source: Note :\n> Minimum Node Version 18.20.*+ required\n\n\n- Install ERPNext using the [official installation guide](https://github.com/frappe/bench#installation).\n\n**To Install ERPNext to your bench:**\n\n```sh\n\tbench get-app --branch version-15 erpnext https://github.com/frappe/erpnext.git\n```\n**To Install Frappe HR to your bench:**\n\nFrappe HR is a dependency for employee management reports in URY\n\n```sh\n\tbench get-app --branch hrms https://github.com/frappe/hrms.git\n```\n\n**Install the URY app to your bench:**\n\n```sh\n\tbench get-app ury https://github.com/ury-erp/ury.git\n```\n**Create New site :**\n\n```sh\n\tbench new-site sitename\n```\n**Install ERPNext to the site :**\n\n```sh\n\tbench --site sitename install-app erpnext\n```\n**Install Frappe HR to the site :**\n\n```sh\n\tbench --site sitename install-app hrms\n```\n\n**Install URY app to the site :**\n\n```sh\n\tbench --site sitename install-app ury\n```\n\n**Build the site :**\n\n```sh\n\tbench --site sitename build\n```\n\n**Migrate the site :**\n\n```sh\n\tbench --site sitename migrate\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include MANIFEST.in\ninclude requirements.txt\ninclude *.json\ninclude *.md\ninclude *.py\ninclude *.txt\nrecursive-include ury *.css\nrecursive-include ury *.csv\nrecursive-include ury *.html\nrecursive-include ury *.ico\nrecursive-include ury *.js\nrecursive-include ury *.json\nrecursive-include ury *.md\nrecursive-include ury *.png\nrecursive-include ury *.py\nrecursive-include ury *.svg\nrecursive-include ury *.txt\nrecursive-exclude ury *.pyc"
  },
  {
    "path": "README.md",
    "content": "\n# URY - Open Source Restaurant Management System\n\nURY is an open source ERP designed to simplify and streamline restaurant operations. It is built on top of  world's best free and open source ERP, ERPNext.\n\n<div align=\"center\">\n\t<a href=\"https://frappecloud.com/dashboard/signup?product=ury\" target=\"_blank\">\n\t\t<picture>\n\t\t\t<source media=\"(prefers-color-scheme: dark)\" srcset=\"https://frappe.io/files/try-on-fc-white.png\">\n\t\t\t<img src=\"https://frappe.io/files/try-on-fc-black.png\" alt=\"Try on Frappe Cloud\" height=\"28\" />\n\t\t</picture>\n\t</a>\n</div>\n\n\n> :warning: Warning : \n> URY is currently in active development, and we are continuously making changes, updates, and working on new features and improvements. Please be aware that until a stable release is reached, backward compatibility is not guaranteed. We make every effort to maintain compatibility.\n\n> :information_source: Note :\n> Our system has been successfully running at scale, serving over 10+ outlets for the past 10 months.\n\n\n## What It Includes\n- **POS**: Dine‑in, takeaway, delivery, offline mode, printer management  \n- **Kitchen Display**: Real‑time order queues, KOT printing  \n- **Analytics**: P&L dashboard, consumption reports, item trends  \n\nGiven below is the list of features of URY app. \n\n### URY POS\n\n**URY POS** is a light weight and easy to use web-based application designed for streamlined order management. It serves as an efficient tool for both cashiers and captains, facilitating order processing at the cash counter and tables.It supports various order types, including dine-in, delivery, takeout and Aggregator. URY POS is compatibile with a wide range of devices, including desktops, tablets, and smartphones. \n\n:information_source: **Note:**  \n> To access the previous version of the separate URY POS app, [click here](https://github.com/ury-erp/pos).  \n> **Use the URY branch `v1` to access these separate apps.**\n> **Support for this version will end in December 2025.**\n\n### URY MOSAIC\n\n**URY MOSAIC** is an interactive Kitchen Display System (KDS) designed to simplify order management in both single and multi-kitchen restaurants. Additionally, it offers optional Kitchen Order Ticket (KOT) printing support for added convenience.\n\n:information_source: **Note:**  \n> To access the previous version of the separate URY MOSAIC app, [click here](https://github.com/ury-erp/mosaic).  \n> **Use the URY branch `v1` to access these separate apps.**\n> **Support for this version will end in December 2025.**\n\n### Daily P & L and Reports\n URY has daily P & L and various reports. It helps restaurants to monitor daily Profit and Loss (P&L), utility consumption, disposables usage, and other key metrics with precision and ease. It provides restaurants with crucial data, enabling timely decision-making by presenting essential information and insights.\n \n:information_source: **Note:**  \n> To access the previous version of the separate URY PULSE app, [click here](https://github.com/ury-erp/pulse).  \n> **Use the URY branch `v1` to access these separate apps.**\n> **Support for this version will end in December 2025.**\n\n## Features\n\n### POS & Billing\n* Role-based access with strict operational controls\n* Pre-billing checklists to enforce compliance (e.g., stock check, hygiene checklist)\n* Linked with stock and accounting modules\n* Multi-format support: Table service, QSR, and takeaway\n* Multi-cashier handling and terminal controls\n* Advanced filters for order and bill management\n* Modern, fast UI with guided flow\n* Shift opening, closing, and cash reconciliation built-in\n\n\n###  Menu & Recipe Management\n* Centralized menu with outlet-level control\n* Recipe mapping using Bill of Materials (BOM)\n* Control pricing, availability, and portions per outlet\n* Supports combos, modifiers, and item bundles\n* Integrated with production planning for daily prep\n\n### Table Order Management\n* Mobile-first order taking for waitstaff\n* Live sync with kitchen and cashier\n* Real-time inventory checks before order placement\n* Supports modifiers, course sequencing, and notes\n* Seamless integration with billing and KDS\n\n\n### Kitchen Display & KOT Management\n* Supports multiple kitchens with advanced printer routing\n* Interactive KDS with live status updates (Preparing, Ready, Served)\n* Delay, cancellation, and modification tracking\n* Real-time kitchen analytics\n* Seamless flow from order to service across stations\n\n\n### Operational Red Flags & Alerts\n* Delayed orders and preparation time breaches\n* KOT not started after order placement\n* Unclosed bills and prolonged table occupancy\n* Excessive KOT cancellations and modifications\n* Real-time alerts for operational exceptions\n* Dashboard view for quick issue resolution across outlets\n\n\n\n### Reports & Analytics\n* Daily Profit & Loss\n* Shortage and Excess reporting\n* Course-wise and item-wise performance\n* Captain and staff performance tracking\n* Branch-wise and outlet-wise comparisons\n* Customer-wise sales trends\n* Detailed sales, production, and stock reports\n* Real-time operational insights for better decision-making\n\nFor more comprehensive list of features [go here.](FEATURES.md)\n\n\n## Getting Started\n\nTo start using URY, you need to first install URY and then setup your first restaurant.\n\n1. [URY Installation Guide](INSTALLATION.md).\n\n2. [URY Setup Instructions](SETUP.md).\n\n## Looking for other versions \t\n\n1. Use branch `v1` to use ury [v0.1.0]\n\n## About\n\nURY is developed by [Tridz Technologies Pvt Ltd](https://tridz.com) and supported by [Frappe](http://frappe.io).\n\n## Terms and Conditions\n\nBy using the URY, you agree to use it responsibly and in compliance with applicable laws. URY is built on open-source technology and is provided for your convenience to manage restaurant operations. While we strive to keep the app reliable, it is provided “as is” without any guarantees, and we are not responsible for any misuse or resulting issues.\n\n[Read More](TERMS.md)"
  },
  {
    "path": "SETUP.md",
    "content": "## URY Setup \n\nThis guide takes you step-by-step through setting up URY on top of ERPNext\n\n### Step 1 : Company\n\n- Login into the site and Follow the installation wizard \n\t- Specify the language.\n\t- Provide country , timezone and currency details.\n\t- Create the first user.\n\t- Enter company name, its description, and select a bank account.\n\t- click on 'Complete setup'\n\t\n### Step 2 : Users and Roles\n\n- To manage restaurant operations in URY, you’ll need to set up specific user roles in the ERPNext/Frappe system. Use the [Frappe/ERPNext interface](https://docs.erpnext.com/docs/user/manual/en/adding-users) to create a new user. \n\n- Assign one of the three URY roles to users:\n\t- URY Manager - Responsible for overseeing and managing all restaurant operations.\n\t- URY Captain - Responsible for managing customer orders and table service.\n\t- URY Cashier - Responsible for managing customer orders, table service, and handling payments and POS operations.\n\n- Below are the recommended DocType permissions for URY roles. These permissions cover only the basic restaurant operations needed for URY (such as table service, billing, and order handling). You can extend or modify them later based on your restaurant’s workflow and access needs.\n\t<details>\n\t  <summary>View Role Permissions</summary>\n\t\n\t| Role | Doctype | Perm | Select | Read | Write | Create | Delete | Submit | Cancel | Amend | Print | Email | Report | Import | Export | Share |\n\t| :---- | :---- | :---- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n\t| **URY Captian** | Company | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | Currency | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | Customer | 0 | ✓ | ✓ | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - |\n\t| | Item Price | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | POS Invoice | 0 | ✓ | ✓ | ✓ | ✓ | - | - | - | - | ✓ | - | - | - | - | - |\n\t| | POS Opening | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | URY KOT | 0 | ✓ | ✓ | ✓ | ✓ | - | ✓ | ✓ | - | - | - | - | - | - | - |\n\t| | URY KOT Error Log | 0 | ✓ | ✓ | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - |\n\t| **URY Cashier** | Company | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | Currency | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | Customer | 0 | ✓ | ✓ | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - |\n\t| | Item Price | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | POS Profile | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | POS Invoice | 0 | ✓ | ✓ | ✓ | ✓ | - | ✓ | ✓ | - | - | - | - | - | - | - |\n\t| | POS Opening | 0 | ✓ | ✓ | ✓ | ✓ | - | ✓ | - | - | - | - | - | - | - | - |\n\t| | POS Closing | 0 | ✓ | ✓ | ✓ | ✓ | - | ✓ | - | - | - | - | - | - | - | - |\n\t| | Sales Invoice | 0 | ✓ | ✓ | ✓ | ✓ | - | ✓ | - | - | - | - | - | - | - | - |\n\t| | User | 0 | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - | - |\n\t| | User | 1 | - | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - | - |\n\t| | URY KOT | 0 | ✓ | ✓ | ✓ | ✓ | - | ✓ | ✓ | - | - | - | - | - | - | - |\n\t| | URY KOT Error Log | 0 | ✓ | ✓ | ✓ | ✓ | - | - | - | - | - | - | - | - | - | - |\n\t\n\t</details>\n\n### Step 3 : Branch\n\n- Create [Branch](https://frappehr.com/docs/v14/en/branch) in ERPNext.\n\n\tBranch setup manage users, POS access, and aggregator configurations for delivery platforms, ensuring smooth operations.\n\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/Branch.png\" style=\"margin: 2rem 2rem;\" alt=\"Branch\">\n\t</div>\n\n- Specify branch users in the table; note that only these users can access the  POS of that branch.\n\t- **Aggregator Settings** : If your restaurant uses aggregator platforms (such as food delivery services), configure the following details:\n\t\t- Customer : Create or link the customer profile for aggregator orders.\n\t\t- Price List : Create a Selling type price list for the aggregator and ensure that item prices are added to this list so they appear correctly in the menu.\n\t\t- Mode of Payment : Set up a mode of payment for aggregator transactions.\n\t- **Keep Sales Invoice Unpaid** : Check this box if you want the aggregator's sales invoice to remain unpaid.\n\t- **Create Invoice without Tax** : Check this box if you want the aggregator's invoice to be created without tax. This applies to both Sales Invoice and POS Invoice.\n\n\n### Step 4 : URY Restaurant\n\n- Go to the \"URY Restaurant List\" and create a new restaurant with the following details:\n\n\tThe restaurant setup links your company, branch, and menu together, defining details like tax templates, invoice series, and menu configurations for rooms and order types.\n\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/develop/DEMO/URY%20Restaurant.png\" style=\"margin: 2rem 2rem;\" alt=\"URY Restaurant\">\n\t</div>\n\n\t- **Name** : Restaurant name\n\t- **Company**: Specify the company under which the restaurant is being created.\n\t- **Invoice Series Prefix**: allows you to define prefix for naming of an Invoice .\n\t- **Aggregator Series Prefix**: allows you to define prefix for naming of a aggregator Invoice.\n\t- **Branch** : Select the branch associated with the restaurant .\n\t- **Default Tax Template** : Mention the [Sales tax](https://docs.erpnext.com/docs/user/manual/en/sales-taxes-and-charges-template) value if applicable.\n\t- **Address** : Provide the address of the restaurant.\n\t- **Default Menu** : Select Menu against the restaurant.\n\t- **Room Wise Menu** : To enable room wise menu.\n\t- **Menu For Room** : Add restaurant menu against each room to handle room wise price list. \n\t- **Order Type Wise Menu** : To enable order type wise menu for cashier.\n\t- **Menu For Order Type** : Add restaurant menu against each order type to handle order type wise price list.\n\n### Step 5 : URY Room\n\n- Next is to Create Restaurant Room with the following details :\n\n\tRooms help organize your restaurant layout—such as indoor, outdoor, or VIP areas and define where orders and print actions (like bills or KOTs) are directed. Each room can have its own printer setup to manage room-wise printing. Make sure to add the room to the corresponding URY Restaurant.\n\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/URY%20Room.png\" style=\"margin: 2rem 2rem;\" alt=\"URY Room\">\n\t</div>\n\n\t- **Name** : Specify a unique name to the room.\n\t- **Room Type** : Select the type from the list.\n\t- **Print Settings** : Choose a network printer.\n\t- **Bill** : Enable for Invoice Printing .\n\t- **KOT Print** : Enable for KOT Printing .\n\n### Step 6 : Item\n\n- Create [Item](https://docs.erpnext.com/docs/user/manual/en/item) to be included in the URY Menu.\n- If an item is sold in a bundle, consider using the [Product Bundle](https://docs.erpnext.com/docs/user/manual/en/product-bundle) feature.\n\n\n### Step 7 : URY Menu\n\n- Create Restaurant Menu From \"URY Menu List\" with the following details:\n\n\tMenu define the list of items available for order, their prices, and how they’re displayed in the POS.\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/develop/DEMO/URY%20Menu.png\" style=\"margin: 2rem 2rem;\" alt=\"URY Menu\">\n\t</div>\n\n\t- **Name** : Specify a unique name to the menu .\n\t- **Restaurant** : Linked to URY Restaurant to select restaurant .\n\t- **Branch** : This field will be automatically populated when you select a restaurant.\n\t- **Enabled** : Activate the checkbox to enable the menu.\n\t- **Items** : List the items included in the menu and their respective rates.\n\t- **Special Dish** : You can use this checkbox in the Item table to show an item as a `Special Items` or `Priority` item for menu display.\n\t- **Disabled** : You can use this checkbox to remove item from menu as per need.\n\t- **Course** : Categorize each item based on the course type (e.g., Starters, Mains, Desserts). In POS, the menu can be categorized and viewed by Course. If the **Indicate in KDS** checkbox is enabled for a course, the Kitchen Display System (KDS) will use the serving priority to determine the preparation and serving order of items.\n\n\tExample:\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/develop/DEMO/URY%20Menu%20Course.png\" style=\"margin: 2rem 2rem;\" alt=\"URY Menu Course\">\n\t</div>\n\n### Step 8 : URY Table\n\n- Create tables for the restaurant in the \"URY Table List\" with the following details:\n\n\tTables define the seating layout, their availability and are visually displayed on the POS for easy selection.\n\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/URY%20Table.png\" style=\"margin: 2rem 2rem;\" alt=\"URY Table\">\n\t</div>\n\n\t- **Name** : Specify the table name that will be listed in URY Order.\n\t- **Restaurant** : Select the associated restaurant.\n\t- **Restaurant Room** : Specify a room to which the table belong ( if no room in restaurant , create a default room and select it for all)\n\t- **Branch** : This field will be auto-populated when the restaurant is selected.\n\t- **No of seat** : Input the number of seats at the table.\n\t- **Minimum seating** : Specify minimum seating capacity .\n\t- **Is Take Away** : For take away orders ( Order type will be \"Take Away\") .\n\t- **Active info** : Record table status and time . Both are read-only .\n\t- **Table Shape** : Use this option to add the table shape for display on the POS screen.\n\n\n### Step 9 : POS Profile\n    \n- [Create POS Profile](https://docs.erpnext.com/docs/user/manual/en/pos-profile) in ERPNext with the following additional fields:\n        \n- **Printer Info**\n\n\t- Configure printing options for invoices and KOTs. For network printers, select the printer in the printer settings table. For QZ printing, enable QZ Print and enter the host IP.\n   \n\t <p>\n\t\t<div style=\"text-align: center;\">\n\t\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/develop/DEMO/POS%20Profile%20Network%20Printer.png\" style=\"margin: 2rem 2rem;\" alt=\"POS Profile QZ\">\n\t\t</div>\n\t</p>\n\t\n\t- **Printer Settings** : Select a printer, enable Bill for invoice printing and KOT Print for KOT printing. For multiple rooms with separate printers, configure the printer settings in the corresponding URY Room for room-wise printing.\n\n\t <p>\n\t\t<div style=\"text-align: center;\">\n\t\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/develop/DEMO/POS%20Profile%20QZ.png\" style=\"margin: 2rem 2rem;\" alt=\"POS Profile QZ\">\n\t\t</div>\n\t</p>\n\t\n\t- **QZ Print** : Check this box to enable QZ printing.\n\t- **QZ Host** : Enter the Network IP for QZ printing.\t\n\n- **URY POS Restrictions**\n\n\t- Set role-based permissions, cashier access, table order restrictions, and operational settings such as attention time, discount, daily POS closing, and visibility of paid invoices.\n\n\t<p>\n\t\t<div style=\"text-align: center;\">\n\t\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/POS%20Profile%20Restrictions.png\" style=\"margin: 2rem 2rem;\" alt=\"POS Profile Restrictions\">\n\t\t</div>\n\t</p>\n\n\t- **Captain Transfer Role Permissions** : Roles added to this field will have permission for 'Captain Transfer'. Users with this role will also have access to all tables.\n\t- **Role Allowed For Billing** : Users assigned this role will function as cashiers in URY POS, responsible for managing billing transactions.\n\t- **Role Restricted For Table Order** : Users with this role have restricted access to table order functions.\n\t- **Table Attention Time** : To indicate \"Attention\" status in the table of URY POS.\n\t- **Show Limited Paid Invoices** : Set a limit for the cashier to view a restricted number of recently paid invoices.\n\t- **Allow Cashier To View All Status** : Enables Cashiers to view all statuses (Paid, Consolidated, Return Invoices) in the recent order.\n\t- **Allow Cashier To Edit And Remove Table Order Items** : To permit cashier to edit and remove table orders.\t\n\t- **Show Item Image In URY POS** : To show image in URY POS.\n\t- **Require Daily POS Closing** : Validate that the previous day’s POS is closed before opening a new one.\n\t- **Enable Discount** : Enable discount feature in URY POS.\n\t- **Enable Order Type Edit** : Use this option to change the order type of an existing invoice.\n\t\t\t\n- **Multiple Cashier Configuration**\n\t- **Enable Multiple Cashier** : Enable this checkbox if the outlet has multiple cashiers. Then, add the cashiers under 'Applicable for Users' and enable the 'Main Cashier' checkbox for the head cashier.\n\t\t\t\n- **KOT Settings**\n\n\t- Configure how Kitchen Order Tickets (KOTs) are managed and monitored, including naming series, timers, reprint and audio alert options, delay notifications with recipients, and daily order number resets to streamline kitchen operations.\n \n\t<p>\n\t\t<div style=\"text-align: center;\">\n\t\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/POS%20Profile%20KOT%20Settings.png\" style=\"margin: 2rem 2rem;\" alt=\"POS Profile KOT Settings\">\n\t\t</div>\n\t</p>\n\t\n\t- **URY KOT Naming Series** : Add a naming series for KOT. A KOT will be created only if a naming series is set.\n\t- **KOT Warning Time** : Timer against KOT are set in this field to trigger a warning when it's exceeded in KDS.\n\t- **Enable KOT Reprint** : Use this option if you need the reprint feature for KOT prints. Make sure to add the appropriate printers and print format.\n\t- **Enable KOT Audio Alert** : Can enable to play a sound when a KOT is displayed. You can add an audio alert in the `KOT alert sound` attachment field.\n\t- **Notify KOT Delay** : Can enable for KOT delay notification and add recipient roles to the Recipients table.\n\t- **Recipients (By Role)** : Add roles of users to receive KOT delay notifications.\n\t- **Reset Order Number Daily** : Use this option to reset the order number for POS Invoices on a daily basis. Note that this is not the invoice number.\n\t\n> **Note:** Update the Price List in Accounting to restaurant menu price list.\n\n### Step 10: URY Production Unit\n\n- **URY Production Unit**\n \tCreate Production Unit From \"URY Production Unit\" with the following details:\n\n\tProduction Units manage multiple kitchens, each with its own web-based interface for displaying KOTs in the Kitchen Display System (KDS). Printers can also be configured per unit for physical KOTs if needed.\n\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/URY%20Production%20Unit.png\" style=\"margin: 2rem 2rem;\" alt=\"URY Production Unit\">\n\t</div>\n\n  - **Production** : Enter the name for your Production Unit.\n  - **POS Profile** : Select the POS Profile\n  - **Branch** : Auto fetch when pos profile is selected \n  - **Warehouse** :Auto fetch when pos profile is selected.\n  - **Item Groups** :Select Item Groups that belong to the production unit.\n  - **Printers** : Table to configure printing inside production unit.\n    - **Printer** : Select Network printer.\n    - **KOT Print** : Enable for KOT Printing .\n    - **KOT Print Format** : Select print format for KOT .\n    - **Block Takeaway KOT** : Enable for block Takeaway KOT printing .\n\n> **Note:** To access KDS follow the site url with `/URYMosaic/Production%20Unit%20Name`. eg: [https://ury.xxxx.com/URYMosaic/Kitchen](https://ury.xxx.com/URYMosaic/Kitchen)\n\n### Step 11 : User Permissions\n\n- User Permissions control access to specific records in ERPNext. Give [User Permission](https://docs.erpnext.com/docs/user/manual/en/user-permissions) to the user for the documents they need to access, such as:\n\t- POS Profile \n\t- Branch\n\n### Step 12 : Printer Setup\n\n- QZ Printer\n\t-  Add your certificate file is at `ury/public/files/cert.pem`.\n\t- Update the `pos/privateKey.js` for v2 and `urypos/privateKey.js` for v1.\n- Network Printer\n\t- Set up CUPS (Common Unix Printing System) for network printing.\n\t- In Network Printer Settings, add your printers and note their and enter the corresponding printer in the URY Room for invoice printing from POS.\n\n<p>\n\t<div style=\"text-align: center;\">\n\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/Network%20Printer%20Settings.png\" style=\"margin: 2rem 2rem;\" alt=\"Network Printer Settings\">\n\t</div>\n</p>\n\n### Step 13 : Customer Search\n\n- frappe.utils.global_search is used for customer searching ,you have to run the following commands for building search index\n\n```\nbench --site site-name build-search-index \n```\n\nand\n\n```\nbench --site site-name rebuild-global-search \n```\n\n### Step 14: Multiple Cashier Configuration\n\t\n- Follow the steps below to set up and manage multiple cashier operations in URY, allowing multiple cashiers to handle billing under one POS profile with individual transaction control.\n- **Create Cashier User** : Create user with the URY Cashier role.\n- **Assign Rooms** : Assign URY Rooms to users. Users can only access POS for the rooms they are assigned to.\n- **Configure POS Profile** : \n\t- In the Applicable for Users table of the POS Profile, add all cashiers for the POS and enable Main Cashier for the head cashier.\n\t- Enable Multiple Cashier in the Multiple Cashier Configuration.\n- **Workflow**\n\t- POS Opening\n\t\t- The main cashier creates the POS Opening Entry first, followed by sub cashier. The main cashier must always open the POS first.\n\t- Order Processing\n\t\t- Proceed with normal order taking and restaurant operations.\n\t- Sub POS Closing\n\n\t\t<div style=\"text-align: center;\">\n\t\t\t<img src=\"https://raw.githubusercontent.com/ury-erp/ury/refs/heads/develop/DEMO/Sub%20POS%20Closing.png\" style=\"margin: 2rem 2rem;\" alt=\"Sub POS Closing\">\n\t\t</div>\n\n\t\t- Sub cashier creates a Sub POS Closing Entry.\n\t\t- Sub POS Closing Entry is an URY Doctype to reconcile sub cashier transactions separately.\n\t- POS Closing\n\t\t- Main cashier creates a POS Closing Entry\n\n### Step 15: URY Report Setting\n\n- Navigate to **URY Report Settings** in  your site. \n- Click on **Add URY Report Settings**.\n- Under the **Details** tab:\n\t- **Extended Hours** : Enable this if the branch operates after 12 AM.\n\t- **No of Hours** : Enter the number of hours, if extended hours is enabled. \n- Under the **Daily P and L Settings** tab:\n\t- **Buying price List** : Select the buying price list for your branch.\n\t\t- Under **Direct Expenses**:\n\t\t\t- **Burning Materials (Other Consumables)** : Table to list consumables.\n\t\t\t\t- **Material** : Enter the Material (e.g., gas, charcoal).\n\t\t\t\t- **Cost Per Unit** : Specify the cost per unit for each material.\n\t\t\t- **Direct Fixed Expenses** : Table to add list of daily fixed direct expenses.\n\t\t\t\t- **Expense** : Provide the expense name.\n\t\t\t\t- **Amount** : Specify amount for each expense.\n\t\t- Under **Indirect Expenses**:\n\t\t\t- **Electricity Charges**: Enter the electricity charges per unit.\n\t\t\t- **Indirect Fixed Expenses** : Table to list daily fixed indirect expenses.\n\t\t\t\t- **Expense** : Provide the expense name.\n\t\t\t\t- **Amount** : Specify amount for each expense.\n\t\t\t- **Percentage Expenses** : Table to list of expenses as a percentage of sales.\n\t\t\t\t- **Expense** : Provide the expense name.\n\t\t\t\t- **Percentage Type** : Choose the percentage type (Net Sales or Gross Sales).\n\t\t\t\t- **Percent** : Specify the percentage of the selected type.\n\t\t- Under **Employee Costs**:\n\t\t\t- **Employee Costs** : Table to list daily fixed expenses as a part of employee costs.\n\t\t\t\t- **Expense** : Provide the expense name.\n\t\t\t\t- **Amount** : Specify amount for each expense.\n\t- **Depreciation** : Add depreciation amount if applicable.\n\n\n- **Daily Gross Salary Cost is calculated from employees attendance.**\n\t- Follow these steps to set up the payment type and payment amount for employees:\n\n\t#### Step 1:\n\n\t- Navigate to **Employee** in  your site. \n\t- Choose the relevant **Employee**.\n\n\t#### Step 2:\n\n\t- Under the **Salary** tab:\n\t\t- **Payment Type** : Choose between Salary or Daily Wage.\n\t\t- **Payment Amount** : Enter the corresponding payment amount. \n\n\nFollow the [Attendance documentation](https://frappehr.com/docs/v14/en/attendance#3-features) for marking the attendance or use the [Employee Attendance Tool](https://frappehr.com/docs/v14/en/employee-attendance-tool#2-how-to-mark-attendance-using-employee-attendance-tool)\n"
  },
  {
    "path": "TERMS.md",
    "content": "# Terms and Conditions – URY\r\n\r\nBy using **URY** (“the App”), you agree to the following:\r\n\r\n1. **Usage**\r\n\r\n   * The App is provided to help manage restaurant operations, including order management and POS.\r\n   * You are solely responsible for how you use the App. We are **not liable for any misuse, data loss, or damages** arising from your usage.\r\n\r\n2. **Open Source**\r\n\r\n   * URY is built on **Frappe/ERPNext**, which are open-source frameworks.\r\n   * Your use of the App and its open-source components is subject to their respective licenses.\r\n\r\n3. **No Warranty**\r\n\r\n   * The App is provided **“as is” without any warranties** of performance, reliability, or fitness for a particular purpose.\r\n\r\n4. **Limitation of Liability**\r\n\r\n   * We are **not responsible** for any direct, indirect, or consequential damages resulting from the use or inability to use the App.\r\n\r\n5. **Termination**\r\n\r\n   * You may stop using the App at any time.\r\n"
  },
  {
    "path": "URYMosaic/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "URYMosaic/.vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"Vue.volar\", \"Vue.vscode-typescript-vue-plugin\"]\n}\n"
  },
  {
    "path": "URYMosaic/AGENTS.MD",
    "content": "# URYMosaic — Agent Documentation\n\n## 1. Overview\n\n**URYMosaic** is a Vue 3 real-time Kitchen Order Ticket (KOT) display system. It runs on a screen in the kitchen and shows live order tickets as they are placed or modified by the POS. Kitchen staff use it to track what to prepare and mark orders as served.\n\n- **URL:** `/URYMosaic/<production_unit_name>` (e.g., `/URYMosaic/Kitchen`)\n- **Served as:** Frappe web page (`ury/www/URYMosaic.html`)\n- **Build output:** `ury/public/URYMosaic/`\n\nThe production unit name in the URL determines which KOTs are displayed. Each physical kitchen station has its own URL pointing to its production unit.\n\n---\n\n## 2. Tech Stack\n\n| Concern | Library / Version |\n|---|---|\n| UI framework | Vue 3.3.4 (Options API) |\n| Build tool | Vite 4.4.5 |\n| Real-time | socket.io-client 4.5.1 |\n| Layout | masonry-layout 4.2.2 |\n| Styling | Tailwind CSS 3.3.3 |\n| Backend API | frappe-js-sdk 1.3.6 |\n| Routing | vue-router 4 |\n\n---\n\n## 3. Project Structure\n\n```\nURYMosaic/\n├── src/\n│   ├── main.js           ← Vue app entry point\n│   ├── App.vue           ← Root component: mounts Header + KOT\n│   │\n│   ├── components/\n│   │   ├── kot.vue       ← Main KOT display (entire app logic lives here)\n│   │   └── Header.vue    ← Top bar with logo and refresh button\n│   │\n│   ├── views/\n│   │   ├── Home.vue      ← Unused placeholder\n│   │   └── Login.vue     ← Simple login form (Frappe session auth)\n│   │\n│   ├── router/\n│   │   ├── index.js      ← Routes: / → KOT component\n│   │   └── auth.js       ← Login route + auth guard\n│   │\n│   └── assets/\n│       └── logos/        ← mosaic.jpg logo\n│\n├── public/               ← Static assets\n├── index.html            ← HTML entry\n├── vite.config.js\n└── package.json\n```\n\n---\n\n## 4. Key Components\n\n### `kot.vue` — The Core Component\n\nThis is where all business logic lives. It is a large Vue Options API component (~620 lines).\n\n**Data properties:**\n| Property | Purpose |\n|---|---|\n| `kot` | Array of KOT objects fetched from API |\n| `production` | Production unit name (extracted from URL path) |\n| `branch` | Branch code from API response |\n| `kot_channel` | Socket.io channel name: `kot_update_{branch}_{production}` |\n| `kot_alert_time` | Minutes threshold for alerting delayed orders |\n| `audio_alert` | Whether to play sound on new KOT |\n| `daily_order_number` | Whether to show daily order number (vs invoice suffix) |\n| `isOnline` | Network status (drives status message display) |\n\n**Key methods:**\n\n| Method | Purpose |\n|---|---|\n| `auth()` | Gets logged-in Frappe user; shows modal if unauthenticated |\n| `fetchKOT()` | `call.get('ury.ury.api.ury_kot_display.kot_list')` — loads all active KOTs |\n| `serveOrder(kot)` | `call.post(serve_kot)` — marks KOT as served, hides card |\n| `confirmOrder(kot)` | `call.post(confirm_cancel_kot)` — acknowledges a cancelled KOT |\n| `rotateCard(kot)` | Toggles overlay on KOT card (reveals Serve/Confirm button) |\n| `toggleItemStrikeThrough(kotitem, kot)` | Marks individual items as done (stored in localStorage) |\n| `updateColorandTable(kot, …)` | Sets card background colour based on order type/status |\n| `updateTimeRemaining()` | Recalculates elapsed time for all KOTs |\n| `orderDelayNotify(kot)` | Sends delay notification when KOT reaches alert threshold |\n| `masonryLoading()` | Re-runs Masonry layout after DOM updates |\n| `handleOnline/Offline()` | Network status handlers — refreshes KOTs on reconnect |\n\n**KOT card colours:**\n- White → Dine-in\n- `bg-blue-100` → Takeaway\n- `bg-[#FFD493]` → Order Modified\n- `bg-[#FFD2D2]` → Cancelled / Partially cancelled\n\n### `Header.vue`\nMinimal header with logo and a refresh button that calls `window.location.reload()`.\n\n---\n\n## 5. API Interactions\n\nAll API calls use a `FrappeApp` instance initialised with the current page URL:\n\n```javascript\nconst frappe = new FrappeApp(url);  // url = window.location.origin\nconst call = frappe.call();\n```\n\n**Endpoints called:**\n\n| Endpoint | Method | Purpose |\n|---|---|---|\n| `ury.ury.api.ury_kot_display.get_site_name` | GET | Gets Frappe site name for Socket.io namespace |\n| `ury.ury.api.ury_kot_display.kot_list` | GET | Returns all active KOTs + branch config |\n| `ury.ury.api.ury_kot_display.serve_kot` | POST | Mark a KOT as served |\n| `ury.ury.api.ury_kot_display.confirm_cancel_kot` | POST | Acknowledge cancelled KOT |\n| `ury.ury.api.ury_kot_notification.order_delay_notification` | POST | Trigger delay notification |\n\n**`kot_list` response structure:**\n```json\n{\n  \"message\": {\n    \"Branch\": \"Main Branch\",\n    \"KOT\": [...],\n    \"kot_alert_time\": 15,\n    \"audio_alert\": 1,\n    \"daily_order_number\": 1\n  }\n}\n```\n\nEach KOT object contains: `name`, `time`, `type`, `restaurant_table`, `table_takeaway`, `is_aggregator`, `customer_name`, `aggregator_id`, `order_no`, `invoice`, `comments`, `production`, `kot_items[]`.\n\n---\n\n## 6. Data Flow\n\n```\n1. mounted() called\n   │\n   ├── fetchAndSetSiteName() → window.globalSiteName\n   ├── initializeSocket()    → connects socket.io to site namespace\n   ├── auth()                → verifies Frappe session\n   └── fetchKOT()\n         │\n         ├── Sets: branch, kot_alert_time, audio_alert, daily_order_number, kot_channel\n         ├── this.kot = result.message.KOT\n         └── updateQtyColorTable() → updateTimeRemaining() → masonryLoading()\n\n2. Socket.io listener: socket.on(kot_channel, doc)\n   │\n   ├── Play audio if enabled\n   ├── this.kot.unshift(doc.kot)   ← prepend new KOT\n   ├── masonryLoading()\n   ├── updateQtyColorTable()\n   └── updateTimeRemaining()\n\n3. setInterval(updateTimeRemaining, 60000)  ← every 60 seconds\n```\n\n---\n\n## 7. Extension Points\n\n**Adding a new KOT type / colour:**\n- `updateColorandTable()` in `kot.vue` — add a new `else if` condition.\n\n**Adding a new action button:**\n- Add button inside the `.isRotated` overlay div in the KOT card template.\n- Wire to a new method that calls the appropriate Frappe API.\n\n**Adding new KOT data fields to the display:**\n- The `kot_list` API (`ury/ury/api/ury_kot_display.py`) returns the raw KOT fields.\n- Add the field to the KOT doctype if needed, return it from `kot_list`, then render in the template.\n\n**Changing the production unit filter:**\n- The URL path segment becomes `this.production` via `decodeURIComponent(parts[parts.length - 1])`.\n- KOTs are filtered server-side by `kot.production === this.production` on the client.\n- To filter differently, modify `kot_list` in `ury_kot_display.py`.\n\n**Changing layout:**\n- Masonry grid: `.grid` div with `.masonry-item` children. Adjust `cols-` Tailwind classes on the grid.\n- Masonry is re-initialised on every data change via `masonryLoading()` — always call this after updating `this.kot`.\n\n---\n\n## 8. Rules for Modifying Safely\n\n### Socket channel naming\nThe channel name is `kot_update_{branch}_{production}`. This string must match what the backend publishes in `ury_kot_display.py` when broadcasting KOT updates. Do not change either side independently.\n\n### Production unit in URL\nThe `production` value comes from the URL path. This is case-sensitive and must match the `URY Production Unit` doctype name exactly. Changing URL structure requires updating `website_route_rules` in `ury/hooks.py`.\n\n### KOT `showDiv` flag\nKOTs are hidden from the board by setting `kot.showDiv = true` (not by removing from array). This keeps array indices stable during socket updates. Do not filter the `kot` array reactively.\n\n### LocalStorage strike-through state\n`toggleItemStrikeThrough` persists `{kotName}_{itemName}_strike` keys in localStorage. `removeAllItemsFromLocalStorage` cleans up after a KOT is served/confirmed. Ensure any KOT removal also calls this cleanup.\n\n### Audio alerts\n`audio_alert` is fetched from the backend (`POS Profile` / `URY Printer Settings`). Audio only plays if `audio_alert === 1` AND the user has clicked the page at least once (browser autoplay policy). The `showAudioAlertMessage` banner guides users to click.\n\n### Authentication\nIf `auth()` fails, `this.showModal = true` shows a \"Not Permitted / Login\" modal. The redirect goes to `/login?redirect-to=URYMosaic/{production}`. Do not remove this auth check.\n\n### What NOT to change\n- The `kot_channel` computation — changing it breaks real-time updates.\n- The `frappe.call()` initialisation pattern — `url` must be the site origin, not a path.\n- `masonryLoading()` call pattern — always call it via `this.$nextTick()` inside to avoid layout before DOM updates.\n- The `kot.isRotated` card flip pattern — it is the only user interaction to reveal action buttons; modifying it affects kitchen UX.\n"
  },
  {
    "path": "URYMosaic/CLAUDE.MD",
    "content": "AGENTS.MD\n"
  },
  {
    "path": "URYMosaic/README.md",
    "content": "# Vue 3 + Vite\n\nThis template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.\n\n## Recommended IDE Setup\n\n- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).\n"
  },
  {
    "path": "URYMosaic/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/ury.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>URY Mosaic</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script>window.csrf_token = '{{ frappe.session.csrf_token }}';</script>\n    <script type=\"module\" src=\"/src/main.js\"></script>\n  </body>\n</html>"
  },
  {
    "path": "URYMosaic/package.json",
    "content": "{\n  \"name\": \"urymosaic\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build --base=/assets/ury/URYMosaic/ && yarn copy-html-entry\",\n    \"preview\": \"vite preview\",\n    \"copy-html-entry\": \"cp ../ury/public/URYMosaic/index.html ../ury/www/URYMosaic.html\"\n  },\n  \"dependencies\": {\n    \"socket.io-client\": \"^4.5.1\",\n    \"vue\": \"^3.3.4\",\n    \"vue-router\": \"^4\",\n    \"autoprefixer\": \"^10.4.15\",\n    \"axios\": \"^1.5.0\",\n    \"flowbite\": \"^1.8.1\",\n    \"flowbite-vue\": \"0.0.17-next.6\",\n    \"frappe-js-sdk\": \"^1.3.6\",\n    \"masonry-layout\": \"^4.2.2\",\n    \"postcss\": \"^8.4.30\",\n    \"tailwindcss\": \"^3.3.3\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^4.2.3\",\n    \"autoprefixer\": \"^10.4.16\",\n    \"postcss\": \"^8.4.30\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"vite\": \"^4.4.5\"\n  }\n}\n"
  },
  {
    "path": "URYMosaic/postcss.config.js",
    "content": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "URYMosaic/proxyOptions.js",
    "content": "const common_site_config = require('../../../sites/common_site_config.json');\nconst { webserver_port } = common_site_config;\n\nexport default {\n\t'^/(app|api|assets|files)': {\n\t\ttarget: `http://localhost:${webserver_port}`,\n\t\tws: true,\n\t\trouter: function(req) {\n\t\t\tconst site_name = req.headers.host.split(':')[0];\n\t\t\treturn `http://${site_name}:${webserver_port}`;\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "URYMosaic/src/App.vue",
    "content": "<template>\n\t<Header/>\n \t<div class=\"bg-slate-300 min-h-screen\"><KOT /></div>\n</template>\n\n<script>\nimport KOT from './components/kot.vue';\nimport Header from \"./components/Header.vue\";\n\nexport default {\n\t\n\tname: \"app\",\n\tcomponents: {\n\t\tKOT,\n\t\tHeader,\n\t},\n};\n</script>\n\n"
  },
  {
    "path": "URYMosaic/src/components/Header.vue",
    "content": "<template>\n\n    <header class=\"bg-white p-4 flex justify-between items-center\">\n    <div class=\"flex items-center\">\n        <img :src=\"imagePath\" alt=\"Logo\" class=\"ml-20 w-40 h-15 mr-2\">\n       \n    </div>\n    <div class=\"flex items-center\">\n\n\n        <button class=\" hover:bg-slate-300 text-blue font-semibold px-6 py-1 rounded-md\" @click=\"reloadKOT\">\n          <svg class=\"w-6 h-6 text-blue-800 dark:text-blue\" aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 18 20\">\n    <path stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97\"/> Refresh\n  </svg> \n            \n        </button>\n    </div>\n</header>\n\n\n</template>\n\n<script>\n\nimport uriMosaicImage from \"@/assets/logos/mosaic.jpg\";\n// import KOT from './kot.vue';\n\nexport default {\n  name: \"Header\",\n  setup() {\n    \n  },\n  data() {\n    return {\n      imagePath: uriMosaicImage,\n    };\n  },\n  methods:{\n    reloadKOT(){\n      // KOT.methods.fetchKOT(this);\n      window.location.reload();\n    }\n  },\n  computed: {\n    \n  },\n};\n</script>\n"
  },
  {
    "path": "URYMosaic/src/components/kot.vue",
    "content": "<template>\n  <div class=\"mx-auto p-6 mb-16 relative\">\n    <!-- Alert Modal div start-->\n    <div\n      v-if=\"this.showModal\"\n      class=\"fixed inset-0 z-10 overflow-y-auto bg-gray-100\"\n    >\n      <div class=\"flex items-center justify-center\">\n        <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n          <p\n            class=\"block text-left text-xl font-medium text-gray dark:text-gray\"\n          >\n            <span\n              class=\"w-3 h-3 rounded-full inline-block mr-1 bg-red-500\"\n            ></span>\n            Not Permitted\n          </p>\n          <hr class=\"border-gray-200\" />\n\n          <p class=\"text-left text-xl mt-6 font-medium text-gray-500\">\n            Log in to access this page.\n          </p>\n\n          <div class=\"flex justify\">\n            <button\n              @click=\"\n                this.showModal = false;\n                this.redirectToLogin();\n              \"\n              class=\"mt-8 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n            >\n              Login\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n    <!-- Alert Modal div end-->\n\n    <div\n      class=\"grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\"\n    >\n      <div v-for=\"kot in this.kot\" :key=\"kot.name\">\n        <div\n          :class=\"[kot.color]\"\n          class=\"inline-block shadow-lg gap-4 p-3 rounded-2xl w-90 h-auto masonry-item\"\n          style=\"margin-top: 28px\"\n          v-if=\"!kot.showDiv && kot.production === production\"\n        >\n          <div class=\"w-64 check\">\n            <div\n              :class=\"[{ hidden: !kot.isRotated }]\"\n              @click=\"rotateCard(kot)\"\n              class=\"absolute inset-0 bg-white z-50 opacity-80 rounded-2xl flex flex-col justify-center items-center\"\n            >\n              <button\n                @click=\"\n                  kot.type === 'Cancelled' || kot.type === 'Partially cancelled'\n                    ? confirmOrder(kot)\n                    : serveOrder(kot)\n                \"\n                :class=\"[{ hidden: !kot.isRotated }]\"\n                class=\"py-2 px-6 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition duration-300 ease-in-out\"\n              >\n                {{\n                  kot.type === \"Cancelled\" || kot.type === \"Partially cancelled\"\n                    ? \"Confirm\"\n                    : \"Serve\"\n                }}\n              </button>\n            </div>\n\n            \n              <!-- Serve Button -->\n\n              <!-- Card Header: Table Name and Order Number -->\n              <div class=\"flex justify-between\" @click=\"rotateCard(kot)\">\n                <div class=\"text-sm w-48\">\n                  <span\n                    v-if=\"kot.tableortakeaway !== 'Takeaway'\"\n                    class=\"text-sm font-medium text-[#6B7280]\"\n                    >Table\n                  </span>\n                  <span class=\"text-black-500 font-semibold\">\n                    {{ kot.tableortakeaway }}\n                    <span class=\"text-sm font-medium text-[#6B7280]\"\n                      >( {{ kot.user }} )</span\n                    ></span\n                  ><br />\n                  <span v-if=\"kot.is_aggregator\" class=\"text-sm font-medium text-[#6B7280]\">Aggregator</span>\n                  <span v-if=\"kot.is_aggregator\" class=\"text-black-500 ml-2 font-semibold\"\n                    >{{ kot.customer_name }}\n                  </span><br v-if=\"kot.is_aggregator\" />\n                  <span v-if=\"kot.is_aggregator\" class=\"text-sm font-medium text-[#6B7280]\">Aggregator ID</span>\n                  <span v-if=\"kot.is_aggregator\" class=\"text-black-500 ml-2 font-semibold\"\n                    >{{ kot.aggregator_id }}\n                  </span><br v-if=\"kot.is_aggregator\"/>\n                  <span class=\"text-sm font-medium text-[#6B7280]\">Order</span>\n                  <span class=\"text-black-500 ml-2 font-semibold\"\n                    >{{ this.daily_order_number ? kot.order_no : kot.invoice.slice(-4) }}\n                    \n                  </span>\n                  <span\n                    class=\"text-black-500 ml-2 font-semibold\"\n                    v-if=\"\n                      kot.type === 'Partially cancelled' ||\n                      kot.type === 'Cancelled'\n                    \"\n                  >\n                    ( {{ kot.type }} )</span\n                  >\n                </div>\n                <div\n                  :class=\"kot.timecolor\"\n                  class=\"font-inter font-semibold text-2xl leading-10\"\n                >\n                  {{ kot.timeRemaining }}\n                </div>\n              </div>\n              <div\n                v-if=\"kot.type === 'Duplicate'\"\n                class=\"text-[#DC0000] font-medium\"\n              >\n                ( Duplicate KOT ( CHECK WITH CAPTAIN ) )\n              </div>\n              <div v-show=\"kot.comments\" class=\"text-[#6B7280] font-medium\">\n                ( {{ kot.comments }} )\n              </div>\n              <div></div>\n              <div>\n                <div\n                  class=\"font-semibold justify-between items-center mt-2\"\n                  v-for=\"kotitem in sortedKotItems(kot)\"\n                  :key=\"kotitem.name\"\n                >\n                  <div\n                    @click=\"\n                      () => {\n                        toggleItemStrikeThrough(kotitem, kot);\n                      }\n                    \"\n                    :class=\"{\n                      'line-through text-green-700': kotitem.striked,\n                    }\"\n                    class=\"flex font-semibold justify-between items-center\"\n                  >\n                    <div>\n                      <span class=\"ml-2 text-black-100\">{{\n                        kotitem.item_name\n                      }}<span v-show=\"kotitem.indicate_course\" class=\"text-sm text-gray-500 ml-1\"> ( {{kotitem.course}} )</span>\n                      </span\n                      ><br />\n                      <span\n                        class=\"ml-2 text-black-100\"\n                        v-if=\"\n                          kot.type === 'Partially cancelled' ||\n                          kot.type === 'Cancelled'\n                        \"\n                        >[Old Qty = {{ kotitem.quantity }}]</span\n                      >\n                    </div>\n                    <div>\n                      <span class=\"ml-2 text-black-100\">{{ kotitem.qty }}</span>\n                    </div>\n                  </div>\n                  <div>\n                    <p\n                      v-show=\"kotitem.comments\"\n                      class=\"ml-2 text-[#6B7280] font-medium\"\n                    >\n                      {{ kotitem.comments }}\n                    </p>\n                    <hr class=\"my-1 border-gray-200 mt-2\" />\n                  </div>\n                </div>\n              </div>\n            \n          </div>\n          <!-- You can add more item/quantity pairs here as needed -->\n        </div>\n      </div>\n    </div>\n\n    <!-- Audio Alert Message -->\n    <div\n      v-if=\"showAudioAlertMessage\"\n      class=\"absolute top-1 left-1/2 transform -translate-x-1/2 p-2 font-bold text-2xl text-red-500 text-center\"\n    >\n      Audio notifications disabled. Click anywhere to enable.\n    </div>\n\n    <div\n      v-if=\"statusMessage\"\n      :class=\"[\n        'fixed',\n        'bottom-10',\n        'right-10',\n        'p-4',\n        'rounded',\n        'text-white',\n        {\n          'bg-green-500': isOnline,\n          'bg-red-500': !isOnline,\n        },\n      ]\"\n      @transitionend=\"handleTransitionEnd\"\n    >\n      {{ statusMessage }}\n    </div>\n  </div>\n</template>\n\n<script>\nimport { FrappeApp } from \"frappe-js-sdk\";\nimport Masonry from \"masonry-layout\";\nimport io from \"socket.io-client\";\n\nlet host = window.location.hostname;\nlet port = window.location.port;\nlet protocol = window.location.protocol;\nlet url = port ? `${protocol}//${host}:${port}` : `${protocol}//${host}`;\nwindow.globalSiteName = '';\nlet socket; \n\nasync function fetchAndSetSiteName() {\n    try {\n        const response = await fetch('/api/method/ury.ury.api.ury_kot_display.get_site_name', {\n            method: 'GET',\n            headers: {\n                'Content-Type': 'application/json'\n            }\n        });\n        const data = await response.json();\n        window.globalSiteName = data.message.site_name;\n        // console.log('Global Site Name:', window.globalSiteName);\n    } catch (error) {\n        console.error('Failed to fetch site name:', error);\n    }\n}\n\nasync function initializeSocket() {\n    await fetchAndSetSiteName();\n    if (window.globalSiteName) {\n        let site = window.globalSiteName;\n        let site_url = `${url}/${site}`;\n        socket = io(site_url,{ withCredentials: true });\n        console.log(\"socket == >\",socket)\n        socket.on('connect_error', (err) => {\n            console.error(\"Socket connection error:\", err);\n        }); \n        socket.on('connect', () => {\n            console.log('Socket connected:', socket.connected);\n        });\n    } else {\n        console.error('Site name is not set. Socket cannot be initialized.');\n    }\n}\n\ninitializeSocket(); // Initialize the socket after fetching the site name\n\n\nconst frappe = new FrappeApp(url);\nexport default {\n  // inject: [\"$auth\", \"$socket\"],\n  data() {\n    return {\n      kot: [],\n      masonry: null,\n      call: frappe.call(),\n      production: \"\",\n      branch: \"\",\n      kot_channel: \"\",\n      clickedItems: new Set(),\n      struckThroughItems: {},\n      loggeduser: \"\",\n      showModal: false,\n      kot_alert_time: \"\",\n      showAudioAlertMessage: false,\n      audio_alert: 0,\n      isOnline: navigator.onLine,\n      statusMessage: \"\",\n      daily_order_number:0\n    };\n  },\n  methods: {\n    playAlertSound(path) {\n      var currentDomain = window.location.origin;\n      var audio_path = currentDomain + path;\n      const audio = new Audio(audio_path);\n      audio.play();\n    },\n    auth() {\n      return new Promise((resolve, reject) => {\n        const auth = frappe.auth();\n        auth\n          .getLoggedInUser()\n          .then((user) => {\n            this.loggeduser = user;\n            resolve();\n          })\n          .catch((error) => {\n            console.error(error);\n            reject(error);\n          });\n      });\n    },\n    fetchKOT() {\n      return new Promise((resolve, reject) => {\n        try {\n          this.call\n            .get(\"ury.ury.api.ury_kot_display.kot_list\", {})\n            .then((result) => {\n              console.log(result,\"..............result\")\n              this.branch = result.message.Branch;\n              this.kot_alert_time = result.message.kot_alert_time;\n              this.audio_alert = result.message.audio_alert;\n              this.daily_order_number = result.message.daily_order_number;\n              this.kot_channel = `kot_update_${this.branch}_${this.production}`;\n              this.kot = result.message.KOT;\n              this.updateQtyColorTable();\n              this.updateTimeRemaining();\n              this.masonryLoading();\n              resolve();\n            })\n            .catch((error) => {\n              console.error(error);\n              reject(error);\n            });\n        } catch (error) {\n          reject(error);\n        }\n      });\n    },\n    rotateCard(kot) {\n      this.masonryLoading();\n      kot.isRotated = !kot.isRotated;\n    },\n    confirmOrder(kot) {\n      const now = new Date();\n      this.currentTime = now.toLocaleTimeString();\n      this.call\n        .post(\"ury.ury.api.ury_kot_display.confirm_cancel_kot\", {\n          name: kot.name,\n          user: this.loggeduser,\n        })\n        .then((result) => {\n          // kot.isHidden = !kot.isHidden;\n          kot.showDiv = !kot.showDiv;\n          // this.showDiv = false;\n\n          this.removeAllItemsFromLocalStorage(kot);\n          this.masonryLoading();\n        })\n        .catch((error) => console.error(error));\n    },\n    async serveOrder(kot) {\n      const now = new Date();\n      this.currentTime = now.toLocaleTimeString();\n\n      this.call\n        .post(\"ury.ury.api.ury_kot_display.serve_kot\", {\n          name: kot.name,\n          time: this.currentTime,\n        })\n        .then((result) => {\n          // kot.isHidden = !kot.isHidden;\n          kot.showDiv = !kot.showDiv;\n          // this.showDiv = false;\n\n          this.removeAllItemsFromLocalStorage(kot);\n          this.masonryLoading();\n        })\n        .catch((error) => console.error(error));\n    },\n\n    async orderDelayNotify(kot) {\n      const now = new Date();\n      this.currentTime = now.toLocaleTimeString();\n\n      this.call\n        .post(\n          \"ury.ury.api.ury_kot_notification.order_delay_notification\",\n          {\n            id: kot.name,\n          }\n        )\n        .then((result) => {\n          // console.log(\"call backed \", result);\n        })\n        .catch((error) => console.error(error));\n    },\n    toggleItemStrikeThrough(kotitem, kot) {\n      kotitem.striked = !kotitem.striked;\n      localStorage.setItem(\n        `${kot.name}_${kotitem.name}_strike`,\n        JSON.stringify(kotitem.striked)\n      );\n    },\n\n    updateColorandTable(kot, restaurant_table, type, table_takeaway) {\n      if (restaurant_table === undefined) {\n        kot.tableortakeaway = \"Takeaway\";\n      } else {\n        if (table_takeaway == 1) {\n          kot.tableortakeaway = \"Takeaway\";\n        } else {\n          kot.tableortakeaway = restaurant_table;\n        }\n      }\n      if (type == \"Order Modified\") {\n        kot.color = \"bg-[#FFD493] border border-[#FFC700]\";\n      } else if (type == \"Partially cancelled\" || type == \"Cancelled\") {\n        kot.color = \"bg-[#FFD2D2] border border-[#FAA7A7]\";\n      } else if (restaurant_table === undefined || table_takeaway == 1) {\n        kot.color = \"bg-blue-100 border border-blue-200\";\n      } else {\n        kot.color = \"bg-white\";\n      }\n      console.log(type,\".............type\")\n    },\n    updateQtyColorTable() {\n      this.kot.forEach((kot) => {\n        console.log(kot,\"kot............\")\n        this.updateColorandTable(\n          kot,\n          kot.restaurant_table,\n          kot.type,\n          kot.table_takeaway\n        );\n\n        kot.kot_items.forEach((kotitem) => {\n          const savedState = localStorage.getItem(\n            `${kot.name}_${kotitem.name}_strike`\n          );\n          if (savedState) {\n            kotitem.striked = JSON.parse(savedState);\n          }\n          this.calculateQty(\n            kotitem,\n            kotitem.quantity,\n            kot.type,\n            kotitem.cancelled_qty\n          );\n        });\n      });\n    },\n    calculateQty(kotitem, qty, type, cancelled_qty) {\n      kotitem.qty = qty;\n      if (type == \"Partially cancelled\" || type == \"Cancelled\") {\n        kotitem.qty = qty - cancelled_qty;\n      }\n    },\n    removeAllItemsFromLocalStorage(kot) {\n      // Get all keys in local storage\n      const keys = Object.keys(localStorage);\n      // Remove keys that start with `${kot.name}_`\n      keys.forEach((key) => {\n        if (key.startsWith(`${kot.name}_`)) {\n          localStorage.removeItem(key);\n        }\n      });\n    },\n\n    updateTimeRemaining() {\n      // console.log(\"update time\", this.kot_channel);\n      this.kot.forEach((kot) => {\n        kot.timeRemaining = this.calculateTimeRemaining(kot.time);\n\n        const timeRemaining = kot.timeRemaining.split(\":\");\n        const minutes =\n          parseInt(timeRemaining[0]) * 60 + parseInt(timeRemaining[1]);\n\n        if (\n          minutes === this.kot_alert_time &&\n          kot.type !== \"Cancelled\" &&\n          kot.type !== \"Partially cancelled\"\n        ) {\n          this.orderDelayNotify(kot);\n        }\n        if (minutes >= this.kot_alert_time) {\n          kot.timecolor = \"text-[#DC0000]\";\n        } else {\n          kot.timecolor = \"text-black\";\n        }\n      });\n    },\n    calculateTimeRemaining(targetTime) {\n      const currentTime = new Date();\n      const [targetHours, targetMinutes, targetSeconds] = targetTime.split(\":\");\n      const targetDate = new Date(\n        currentTime.getFullYear(),\n        currentTime.getMonth(),\n        currentTime.getDate(),\n        targetHours,\n        targetMinutes,\n        targetSeconds\n      );\n\n      const timeDifference = currentTime - targetDate;\n      const hoursRemaining = Math.floor(timeDifference / 3600000);\n      const minutesRemaining = Math.floor((timeDifference % 3600000) / 60000);\n\n      return `${hoursRemaining} : ${minutesRemaining}`;\n    },\n    fetchkotwithmasonry() {\n      return this.fetchKOT().then(() => {\n        this.masonryLoading();\n      });\n    },\n    redirectToLogin() {\n      var currentDomain = window.location.origin;\n      window.location.href =\n        currentDomain + \"/login?redirect-to=URYMosaic/\" + this.production;\n    },\n    masonryLoading() {\n      this.$nextTick(() => {\n        this.masonry = new Masonry(this.$el.querySelector(\".grid\"), {\n          itemSelector: \".masonry-item\",\n          gutter: 28,\n\n          // Other Masonry options can be added here\n        });\n        this.masonry.layout();\n      });\n    },\n    hideAudioAlertMessage() {\n      this.showAudioAlertMessage = false;\n    },\n    handleOnline() {\n      this.isOnline = true;\n      this.setStatusMessage(\"You are online\");\n      this.hideStatusMessageAfterDelay();\n      this.fetchKOT().then(() => {\n        this.masonryLoading();\n      });\n    },\n    handleOffline() {\n      this.isOnline = false;\n      this.setStatusMessage(\"You are Offline\");\n    },\n    setStatusMessage(message) {\n      this.statusMessage = message;\n    },\n    hideStatusMessageAfterDelay() {\n      setTimeout(() => {\n        this.statusMessage = \"\";\n      }, 3000);\n    },\n    handleTransitionEnd() {\n      if (!this.isOnline) {\n        // Reset the status message after transition end\n        this.setStatusMessage(\"\");\n      }\n    },\n  },\n  mounted() {\n    window.addEventListener(\"online\", this.handleOnline);\n    window.addEventListener(\"offline\", this.handleOffline);\n    document.addEventListener(\"click\", this.hideAudioAlertMessage);\n    const currentUrl = window.location.href;\n    const parts = currentUrl.split(\"/\");\n    const production = parts[parts.length - 1];\n    const decodedProduction = decodeURIComponent(production);\n    this.production = decodedProduction;\n    const self = this;\n    window.addEventListener(\"resize\", this.masonryLoading());\n    this.masonryLoading();\n\n    this.auth()\n      .then(() => {\n        self.fetchKOT().then(() => {\n          if (this.audio_alert === 1) {\n            this.showAudioAlertMessage = true;\n          }\n          socket.on(this.kot_channel, (doc) => {\n            if (this.audio_alert === 1) {\n              this.playAlertSound(doc.audio_file);\n            }\n            let kottime = localStorage.getItem(\"kot_time\");\n            if (doc.last_kot_time !== null) {\n              if (doc.last_kot_time !== kottime) {\n                this.fetchKOT().then(() => {\n                  this.masonryLoading();\n                });\n              }\n            }\n            this.kot.unshift(doc.kot);\n            this.masonryLoading();\n            this.updateQtyColorTable();\n            this.updateTimeRemaining();\n            setTimeout(()=>{\n              if (doc.kot.type === \"Cancelled\"){\n                this.fetchKOT().then(() => {\n                  this.masonryLoading();\n                });\n              }\n            },1500)\n            localStorage.setItem(\"kot_time\", doc.kot.time);\n          });\n        });\n      })\n      .catch((error) => {\n        console.error(\"Authentication error:\", error);\n        this.showModal = true;\n      });\n    setInterval(this.updateTimeRemaining, 60000);\n  },\n  beforeDestroy() {\n    window.removeEventListener(\"online\", this.handleOnline);\n    window.removeEventListener(\"offline\", this.handleOffline);\n    document.removeEventListener(\"click\", this.hideAudioAlertMessage);\n  },\n  computed: {\n    sortedKotItems() {\n      return (kot) => {\n        return kot.kot_items.sort((a, b) => a.serve_priority - b.serve_priority);\n      };\n    },\n  },\n};\n</script>\n<style>\n.bg-gray-100 {\n  background-color: rgba(0, 0, 0, 0.2);\n}\n</style>\n"
  },
  {
    "path": "URYMosaic/src/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\t"
  },
  {
    "path": "URYMosaic/src/main.js",
    "content": "import './index.css';\nimport { createApp, reactive } from \"vue\";\nimport App from \"./App.vue\";\n\nimport router from './router';\n\nconst app = createApp(App);\n\n// Plugins\napp.use(router);\n\n// Global Properties,\n// components can inject this\n\n// Configure route gaurds\nrouter.beforeEach(async (to, from, next) => {\n\tif (to.matched.some((record) => !record.meta.isLoginPage)) {\n\t\t// this route requires auth, check if logged in\n\t\t// if not, redirect to login page.\n\t\tif (!auth.isLoggedIn) {\n\t\t\tnext({ name: 'Login', query: { route: to.path } });\n\t\t} else {\n\t\t\tnext();\n\t\t}\n\t} else {\n\t\tif (auth.isLoggedIn) {\n\t\t\tnext({ name: 'Home' });\n\t\t} else {\n\t\t\tnext();\n\t\t}\n\t}\n});\n\napp.mount(\"#app\");\n"
  },
  {
    "path": "URYMosaic/src/router/auth.js",
    "content": "export default [\n\t{\n\t\tpath: '/login',\n\t\tname: 'Login',\n\t\tcomponent: () =>\n\t\t\timport(/* webpackChunkName: \"login\" */ '../views/Login.vue'),\n\t\tmeta: {\n\t\t\tisLoginPage: true\n\t\t},\n\t\tprops: true\n\t}\n]\n"
  },
  {
    "path": "URYMosaic/src/router/index.js",
    "content": "import { createRouter, createWebHistory } from \"vue-router\";\nimport Home from \"../views/Home.vue\";\nimport authRoutes from './auth';\nimport KOT from '../components/kot.vue';\n\nconst routes = [\n  {\n\tpath: \"/\",\n\tname: \"KOT\",\n\tcomponent: KOT,\n  },  \n  ...authRoutes,\n];\n\nconst router = createRouter({\n  base: \"/URYMosaic/\",\n  history: createWebHistory(),\n  routes,\n});\n\nexport default router;\n"
  },
  {
    "path": "URYMosaic/src/style.css",
    "content": ":root {\n  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;\n  line-height: 1.5;\n  font-weight: 400;\n\n  color-scheme: light dark;\n  color: rgba(255, 255, 255, 0.87);\n  background-color: #242424;\n\n  font-synthesis: none;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-text-size-adjust: 100%;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\nbody {\n  margin: 0;\n  display: flex;\n  place-items: center;\n  min-width: 320px;\n  min-height: 100vh;\n}\n\nh1 {\n  font-size: 3.2em;\n  line-height: 1.1;\n}\n\nbutton {\n  border-radius: 8px;\n  border: 1px solid transparent;\n  padding: 0.6em 1.2em;\n  font-size: 1em;\n  font-weight: 500;\n  font-family: inherit;\n  background-color: #1a1a1a;\n  cursor: pointer;\n  transition: border-color 0.25s;\n}\nbutton:hover {\n  border-color: #646cff;\n}\nbutton:focus,\nbutton:focus-visible {\n  outline: 4px auto -webkit-focus-ring-color;\n}\n\n.card {\n  padding: 2em;\n}\n\n#app {\n  max-width: 1280px;\n  margin: 0 auto;\n  padding: 2rem;\n  text-align: center;\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    color: #213547;\n    background-color: #ffffff;\n  }\n  a:hover {\n    color: #747bff;\n  }\n  button {\n    background-color: #f9f9f9;\n  }\n}\n"
  },
  {
    "path": "URYMosaic/src/views/Home.vue",
    "content": "<template>\n  <div>\n\t<h1>Home Page</h1>\n\t<!-- Fetch the resource on click -->\n\t<button @click=\"$resources.ping.fetch()\">Ping</button>\n  </div>\n</template>\n\n<script>\nexport default {\n  resources: {\n\tping() {\n\t  return {\n\t\tmethod: \"frappe.ping\", // Method to call on backend\n\t\tonSuccess(d) {\n\t\t  alert(d);\n\t\t},\n\t  };\n\t},\n  },\n};\n</script>\n"
  },
  {
    "path": "URYMosaic/src/views/Login.vue",
    "content": "<template>\n  <div class=\"min-h-screen bg-white flex\">\n\t<div class=\"mx-auto w-full max-w-sm lg:w-96\">\n\t  <form @submit.prevent=\"login\" class=\"space-y-6\">\n\t\t<label for=\"email\"> Username: </label>\n\t\t<input type=\"text\" v-model=\"email\" />\n\t\t<br />\n\t\t<label for=\"password\"> Password: </label>\n\t\t<input type=\"password\" v-model=\"password\" />\n\n\t\t<button\n\t\t  class=\"bg-blue-500 block text-white p-2 hover:bg-blue-700\"\n\t\t  type=\"submit\"\n\t\t>\n\t\t  Sign in\n\t\t</button>\n\t  </form>\n\t</div>\n  </div>\n</template>\n<script>\nexport default {\n  data() {\n\treturn {\n\t  email: null,\n\t  password: null,\n\t};\n  },\n  inject: [\"$auth\"],\n  async mounted() {\n\tif (this.$route?.query?.route) {\n\t  this.redirect_route = this.$route.query.route;\n\t  this.$router.replace({ query: null });\n\t}\n  },\n  methods: {\n\tasync login() {\n\t  if (this.email && this.password) {\n\t\tlet res = await this.$auth.login(this.email, this.password);\n\t\tif (res) {\n\t\t  this.$router.push({ name: \"Home\" });\n\t\t}\n\t  }\n\t},\n  },\n};\n</script>\n"
  },
  {
    "path": "URYMosaic/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./src/**/*.{html,jsx,tsx,vue,js,ts}\"],\n  theme: {\n    extend: {\n      spacing: {\n        '28': '28px', // Define a custom margin-top value\n      },\n    },\n  },\n  plugins: [],\n}\n\n"
  },
  {
    "path": "URYMosaic/vite.config.js",
    "content": "import path from 'path';\nimport { defineConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\nimport proxyOptions from './proxyOptions';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n\tplugins: [vue()],\n\tserver: {\n\t\tport: 8080,\n\t\tproxy: proxyOptions\n\t},\n\tresolve: {\n\t\talias: {\n\t\t\t'@': path.resolve(__dirname, 'src')\n\t\t}\n\t},\n\tbuild: {\n\t\toutDir: '../ury/public/URYMosaic',\n\t\temptyOutDir: true,\n\t\ttarget: 'es2015',\n\t\t// rollupOptions: {\n\t\t// \texternal: [\"../../assets/alert/MA_Designed_ModifiedGunBlasts_4.wav\"],\n\t\t//   },\n\t},\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"scripts\": {\n    \"ury-pos-install\": \"cd urypos && yarn install --check-files\",\n    \"ury-pos-build\": \"cd urypos && yarn build\",\n    \"ury-mosaic-install\": \"cd URYMosaic && yarn install --check-files\",\n    \"ury-mosaic-build\": \"cd URYMosaic && yarn build\",\n    \"ury-posv2-install\": \"cd pos && yarn install --check-files\",\n    \"ury-posv2-build\": \"cd pos && yarn build\",\n    \"postinstall\": \"yarn ury-pos-install && yarn ury-mosaic-install && yarn ury-posv2-install\",\n    \"build\": \"yarn ury-pos-build && yarn ury-mosaic-build && yarn ury-posv2-build\"\n  },\n  \"devDependencies\": {\n    \"@types/qz-tray\": \"^2.2.2\"\n  }\n}\n"
  },
  {
    "path": "pos/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "pos/AGENTS.MD",
    "content": "# POS Frontend — Agent Documentation\n\n## 1. Overview\n\nThis is the **URY POS v2** — a React 19 single-page application that serves as the primary point-of-sale interface for the URY restaurant management system. It runs as a Frappe web page served at `/pos` and communicates exclusively with the Frappe/ERPNext backend via REST API calls.\n\n**Responsibilities:**\n- Taking and managing restaurant orders (Dine-In, Takeaway, Delivery/Aggregator)\n- Real-time menu display with category/course filtering\n- Cart management with item customization (variants, add-ons, comments)\n- Payment processing with multi-mode split payment support\n- Order history viewing, editing, and cancellation\n- Table management for dine-in\n- Invoice printing (HTML and thermal via QZ Tray)\n- POS opening/closing session tracking\n\n---\n\n## 2. Tech Stack\n\n| Concern | Library / Version |\n|---|---|\n| UI framework | React 19.0.0 + TypeScript 5.7.2 |\n| Build tool | Vite 6.2.0 |\n| State management | Zustand 5.0.6 |\n| Routing | React Router DOM 6.30.1 |\n| Styling | Tailwind CSS 3.4.17 |\n| Backend API | frappe-js-sdk 1.10.0 |\n| Notifications | react-toastify 11.0.5 |\n| Thermal printing | qz-tray 2.2.5 |\n| Icons | lucide-react |\n| Accessible UI primitives | @radix-ui/react-select |\n\nBuild output is written to `../ury/public/pos/` and the HTML entry point is `../ury/www/pos.html`.\n\n---\n\n## 3. Project Structure\n\n```\npos/\n├── src/\n│   ├── main.tsx              # Entry point — initialises i18n then mounts React\n│   ├── App.tsx               # Root component, routing, auth guard\n│   ├── index.css             # Global Tailwind imports\n│   │\n│   ├── pages/\n│   │   ├── POS.tsx           # Main ordering interface (menu + order panel)\n│   │   ├── Orders.tsx        # Order history, cancel, edit, print, pay\n│   │   └── Table.tsx         # Table layout spatial view\n│   │\n│   ├── components/           # All UI components\n│   │   ├── ui/               # Headless/primitive components (Button, Input, Dialog…)\n│   │   ├── Header.tsx        # Top bar: logo, search, user menu\n│   │   ├── Sidebar.tsx       # Left nav: menu categories / courses\n│   │   ├── MenuList.tsx      # Grid of MenuCard items\n│   │   ├── MenuCard.tsx      # Single menu item card\n│   │   ├── OrderPanel.tsx    # Right panel: cart, totals, submit\n│   │   ├── PaymentDialog.tsx # Split-payment modal\n│   │   ├── ProductDialog.tsx # Item variant/add-on customisation modal\n│   │   ├── CustomerSelect.tsx# Customer search + new customer form\n│   │   ├── OrderTypeSelect.tsx # Dine In / Takeaway / Aggregators toggle\n│   │   ├── AggregatorSelect.tsx# Aggregator platform selector\n│   │   ├── TableSelectionDialog.tsx # Table picker for dine-in\n│   │   ├── CommentDialog.tsx # Order-level comment editor\n│   │   ├── POSOpeningDialog.tsx # POS not opened / not closed error screen\n│   │   ├── POSOpeningProvider.tsx  # Checks POS opening entry on mount\n│   │   ├── OrderStatusSidebar.tsx  # Status filter tabs (Draft, Paid…)\n│   │   ├── AuthGuard.tsx     # Redirects unauthenticated users\n│   │   ├── InitialLoader.tsx # Splash screen during boot\n│   │   ├── Spotlight.tsx     # Keyboard shortcut overlay\n│   │   └── LayoutView.tsx    # Spatial table layout renderer\n│   │\n│   ├── store/\n│   │   ├── pos-store.ts      # Primary Zustand store (menu, cart, order state)\n│   │   ├── root-store.ts     # Root store (user, orders list, search)\n│   │   └── slices/\n│   │       ├── auth-slice.ts\n│   │       ├── config-slice.ts\n│   │       └── orders-slice.ts\n│   │\n│   ├── lib/                  # API wrappers and utilities\n│   │   ├── frappe-sdk.ts     # Frappe SDK instance (call, db, auth)\n│   │   ├── menu-api.ts       # getRestaurantMenu, getAggregatorMenu\n│   │   ├── order-api.ts      # syncOrder, cancelOrder, getTableOrder\n│   │   ├── payment-api.ts    # getPaymentModes\n│   │   ├── invoice-api.ts    # printInvoice, makePayment\n│   │   ├── pos-profile-api.ts# getCombinedPosProfile, getCurrencyInfo\n│   │   ├── pos-opening-api.ts# checkPOSOpening\n│   │   ├── customer-api.ts   # searchCustomers, addCustomer\n│   │   ├── aggregator-api.ts # getAggregators\n│   │   ├── table-api.ts      # getTables, getRooms\n│   │   ├── print.ts          # HTML invoice printing\n│   │   ├── print-qz.ts       # QZ Tray thermal print\n│   │   ├── role-utils.ts     # Role permission checks\n│   │   ├── utils.ts          # formatCurrency, cn (classnames)\n│   │   └── storage.ts        # localStorage/sessionStorage helpers\n│   │\n│   ├── data/\n│   │   ├── order-types.ts    # DINE_IN, TAKEAWAY, AGGREGATORS constants\n│   │   ├── doctypes.ts       # Frappe doctype name constants\n│   │   └── menu-data.ts      # Static menu structure types\n│   │\n│   └── i18n/                 # Internationalisation system (see §7)\n│       ├── index.ts\n│       ├── config.ts\n│       ├── loader.ts\n│       ├── resolve-language.ts\n│       └── locales/\n│           ├── en.json\n│           └── fr.json\n│\n├── public/                   # Static assets (favicon, logos)\n├── index.html                # HTML entry point\n├── vite.config.ts\n├── tailwind.config.js\n└── tsconfig.json\n```\n\n---\n\n## 4. Component Architecture\n\n**Patterns used:**\n- **Container / presentational split** — pages (POS.tsx, Orders.tsx) own data-fetching and state; components receive props or subscribe to stores.\n- **Zustand stores** — global state is in `pos-store` (POS session) and `root-store` (auth + orders). Components use `usePOSStore()` and `useRootStore()` hooks.\n- **No prop-drilling beyond one level** — components access the store directly rather than having state threaded through many levels.\n- **Dialog pattern** — modals (PaymentDialog, ProductDialog, CommentDialog) are rendered conditionally with a boolean flag and mounted/unmounted, not hidden via CSS.\n\n**Reusability approach:**\n- `ui/` components are headless: they apply className merging via `cn()` but carry no business logic.\n- Business components (e.g., `CustomerSelect`) contain their own local state and API calls when the logic is highly specific to that widget.\n\n---\n\n## 5. Data Flow\n\n```\nFrappe REST API\n      │\n      ▼\nlib/*.ts API wrappers  (call.get / call.post / fetch)\n      │\n      ▼\nZustand stores  (pos-store, root-store)\n      │\n      ├── pos-store: menu items, cart (activeOrders), POS profile,\n      │             selected table/customer/order type, payment modes\n      │\n      └── root-store: current user, orders list (pagination),\n                      selected order detail, search query\n      │\n      ▼\nReact components subscribe via hooks\n      │\n      ▼\nUI renders\n```\n\n**Key flows:**\n1. **Boot:** `main.tsx` → `initI18n()` → `App.tsx` → `AuthGuard` checks session → `POSOpeningProvider` checks POS opening entry → store hydrates menu + profile.\n2. **Add to cart:** click `MenuCard` → `usePOSStore().addToOrder()` → re-render `OrderPanel`.\n3. **Submit order:** `OrderPanel.handleSubmit` → validate → `syncOrder()` → Frappe API → `resetOrderState()`.\n4. **Payment:** `Orders.tsx` opens `PaymentDialog` → `call.post(make_invoice)` → `fetchOrders()`.\n\n---\n\n## 6. API Integration\n\n**SDK initialisation** (`lib/frappe-sdk.ts`):\n```typescript\nconst frappe = new FrappeApp(import.meta.env.VITE_FRAPPE_BASE_URL);\nexport const call = frappe.call();  // RPC-style POST/GET\nexport const db   = frappe.db();    // CRUD\nexport const auth = frappe.auth();  // login/logout/getLoggedInUser\n```\n\n**Calling a whitelisted Python method:**\n```typescript\n// GET\ncall.get('ury.ury_pos.api.getRestaurantMenu', { pos_profile, room })\n\n// POST\ncall.post('ury.ury.doctype.ury_order.ury_order.sync_order', orderData)\n```\n\n**Error handling pattern:**\nFrappe wraps server errors in `_server_messages` (double-JSON-encoded string). Components parse this:\n```typescript\nconst messages = JSON.parse(error._server_messages);\nconst msg = JSON.parse(messages[0]).message;\n```\n\n**Auth / session:** Session cookie is managed by Frappe. No JWT. The SDK's `auth.getLoggedInUser()` call validates the session; failure redirects to `/login?redirect-to=%2Fpos`.\n\n---\n\n## 7. i18n System\n\n### How it works\n\nTranslations live in `src/i18n/locales/*.json`. A lightweight custom engine (no external library) provides:\n\n- `t(key)` — resolves a dot-notation key against the active locale map.\n- `t(key, params)` — interpolates `{{placeholder}}` tokens.\n- Falls back to the key string itself if a translation is missing (visible but non-breaking).\n\n### Initialisation\n\nIn `main.tsx`, before React mounts:\n```typescript\nimport { initI18n } from './i18n';\ninitI18n().then(() => ReactDOM.createRoot(...).render(...));\n```\n\n`initI18n()` calls `resolveLanguage()` which checks (in priority order):\n1. `window.frappe.boot.lang` — Frappe/ERPNext system language\n2. `localStorage.getItem('ury_language')` — manual override\n3. `'en'` — default fallback\n\n### File locations\n\n```\nsrc/i18n/\n├── config.ts           — DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES map\n├── loader.ts           — dynamic import() with per-language cache\n├── resolve-language.ts — priority-based language resolution\n├── index.ts            — t(), initI18n(), getActiveLanguage()\n└── locales/\n    ├── en.json         — English (source of truth)\n    └── fr.json         — French\n```\n\n### Key namespaces in locale files\n\n| Namespace | Contents |\n|---|---|\n| `common` | Loading, Cancel, Save, Apply, Change… |\n| `header` | Search placeholders, Switch To Desk, Logout… |\n| `menu` | Filter labels (All, Special Items) |\n| `cart` | Empty state, button labels, hints |\n| `order` | Order detail labels, cancel dialog |\n| `payment` | Payment dialog labels, discount, summary |\n| `pos` | POS opening/closing dialog |\n| `customer` | Customer search and form labels |\n| `comment` | Comment dialog |\n| `errors` | All error messages |\n| `success` | All success/info messages |\n\n### Adding a new language\n\n1. Create `src/i18n/locales/<lang-code>.json` copying the structure of `en.json`.\n2. Translate all values (do not change keys).\n3. Add the language to `SUPPORTED_LANGUAGES` in `config.ts`:\n   ```typescript\n   export const SUPPORTED_LANGUAGES = { en: 'English', fr: 'Français', de: 'Deutsch' };\n   ```\n4. The loader will dynamically import the file on demand. No other changes needed.\n\n---\n\n## 8. Design Principles\n\n**Naming conventions:**\n- Components: PascalCase, one component per file named identically.\n- Zustand stores: `use<Name>Store` hooks, state slices in `store/slices/`.\n- API wrappers: `lib/<domain>-api.ts`, exported as named functions (`getX`, `createX`, `syncX`).\n- Constants: SCREAMING_SNAKE_CASE in `data/` files.\n- CSS: Tailwind utility classes only; no custom CSS except in `index.css` and `ui/toast.css`.\n\n**Component boundaries:**\n- Components do not import from other components' subdirectories — they import from `../components/<Name>` or `../components/ui`.\n- Store slices are combined in `root-store.ts` — add new slices there, not as separate stores.\n\n**State handling rules:**\n- POS session state (cart, menu, profile) → `pos-store`.\n- Cross-page state (user, orders list, search) → `root-store`.\n- Transient UI state (dialog open, loading flag) → component `useState`.\n- Never store derived values in state — compute them in component or via selectors.\n\n---\n\n## 9. Guidelines for AI Agents\n\n### Safe to modify\n- Adding new `t()` keys: add to `en.json` and `fr.json` simultaneously, then use in component.\n- UI text changes: always go through `t()`, never hardcode.\n- Adding new components: follow the existing pattern — named export, TypeScript props interface, Zustand for data.\n- Adding new API wrappers: add to the appropriate `lib/*-api.ts` file.\n- Styling changes: Tailwind classes only.\n\n### How to add a new feature\n1. Define new state in the appropriate store slice.\n2. Add API call in `lib/`.\n3. Create or modify component.\n4. Add translation keys to both `en.json` and `fr.json`.\n5. No need to modify `main.tsx`, `App.tsx`, or `vite.config.ts` for typical features.\n\n### What NOT to break\n- **`frappe-sdk.ts`** — do not change how `call`, `db`, `auth` are exported; every API file depends on them.\n- **`pos-store.ts` `resetOrderState()`** — called after every successful order submit/payment; must reset all transient order fields.\n- **`main.tsx` init sequence** — `initI18n()` must resolve before `ReactDOM.render()`.\n- **`AuthGuard`** — do not bypass or remove; it protects the entire app.\n- **`POSOpeningProvider`** — do not remove; it prevents use of POS without a valid opening entry.\n- **Order type constants** (`DINE_IN`, `TAKEAWAY`, `AGGREGATORS`) — these are used in API payloads sent to Frappe; do not rename without updating the backend.\n- **`uniqueId` on cart items** — used as React keys and for `updateQuantity`/`removeFromOrder`; must remain stable per cart item.\n\n### Common pitfalls\n- Frappe API errors come in `error._server_messages` (double-encoded JSON), not `error.message`. Use the existing error parsing pattern in `OrderPanel.tsx`.\n- `posProfile` can be `null` during initialisation. Always null-check before reading `.name`, `.cashier`, etc.\n- QZ Tray printing requires `qz_print === 1` on the POS profile. Check `posProfile.qz_print` before calling `print-qz.ts`.\n- Do not add dependencies to `package.json` without confirming the build still outputs to `../ury/public/pos/`.\n"
  },
  {
    "path": "pos/CLAUDE.MD",
    "content": "AGENTS.MD\n"
  },
  {
    "path": "pos/README.md",
    "content": "# React + TypeScript + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh\n\n## Expanding the ESLint configuration\n\nIf you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:\n\n```js\nexport default tseslint.config({\n  extends: [\n    // Remove ...tseslint.configs.recommended and replace with this\n    ...tseslint.configs.recommendedTypeChecked,\n    // Alternatively, use this for stricter rules\n    ...tseslint.configs.strictTypeChecked,\n    // Optionally, add this for stylistic rules\n    ...tseslint.configs.stylisticTypeChecked,\n  ],\n  languageOptions: {\n    // other options...\n    parserOptions: {\n      project: ['./tsconfig.node.json', './tsconfig.app.json'],\n      tsconfigRootDir: import.meta.dirname,\n    },\n  },\n})\n```\n\nYou can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:\n\n```js\n// eslint.config.js\nimport reactX from 'eslint-plugin-react-x'\nimport reactDom from 'eslint-plugin-react-dom'\n\nexport default tseslint.config({\n  plugins: {\n    // Add the react-x and react-dom plugins\n    'react-x': reactX,\n    'react-dom': reactDom,\n  },\n  rules: {\n    // other rules...\n    // Enable its recommended typescript rules\n    ...reactX.configs['recommended-typescript'].rules,\n    ...reactDom.configs.recommended.rules,\n  },\n})\n```\n"
  },
  {
    "path": "pos/eslint.config.js",
    "content": "import js from '@eslint/js'\nimport globals from 'globals'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport reactRefresh from 'eslint-plugin-react-refresh'\nimport tseslint from 'typescript-eslint'\n\nexport default tseslint.config(\n  { ignores: ['dist'] },\n  {\n    extends: [js.configs.recommended, ...tseslint.configs.recommended],\n    files: ['**/*.{ts,tsx}'],\n    languageOptions: {\n      ecmaVersion: 2020,\n      globals: globals.browser,\n    },\n    plugins: {\n      'react-hooks': reactHooks,\n      'react-refresh': reactRefresh,\n    },\n    rules: {\n      ...reactHooks.configs.recommended.rules,\n      'react-refresh/only-export-components': [\n        'warn',\n        { allowConstantExport: true },\n      ],\n    },\n  },\n)\n"
  },
  {
    "path": "pos/index.html",
    "content": "<!doctype html>\n{% set user_lang = frappe.lang or 'en' %}\n{% set is_rtl = user_lang in ['ar', 'he', 'fa', 'ur', 'ku'] %}\n<html lang=\"{{ user_lang }}\" dir=\"{{ 'rtl' if is_rtl else 'ltr' }}\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/ury.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n    <title>URY POS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script>\n      window.csrf_token = \"{{ csrf_token }}\";\n      if (!window.frappe) window.frappe = {};\n      window.app_name = \"{{ app_name }}\";\n      frappe.boot = JSON.parse({{ boot }});\n    </script>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "pos/package.json",
    "content": "{\n  \"name\": \"pos\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build --base=/assets/ury/pos/ && yarn copy-html-entry\",\n    \"copy-html-entry\": \"cp ../ury/public/pos/index.html ../ury/www/pos.html\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@radix-ui/react-select\": \"^2.2.5\",\n    \"@tailwindcss/postcss\": \"^4.1.11\",\n    \"@types/uuid\": \"^10.0.0\",\n    \"autoprefixer\": \"^10.4.21\",\n    \"class-variance-authority\": \"^0.7.1\",\n    \"clsx\": \"^2.1.1\",\n    \"frappe-js-sdk\": \"^1.10.0\",\n    \"jsrsasign\": \"^11.1.0\",\n    \"lucide-react\": \"^0.525.0\",\n    \"postcss\": \"^8.5.6\",\n    \"qz-tray\": \"^2.2.5\",\n    \"react\": \"^19.0.0\",\n    \"react-dom\": \"^19.0.0\",\n    \"react-router-dom\": \"^6.30.1\",\n    \"react-toastify\": \"^11.0.5\",\n    \"tailwind-merge\": \"^3.3.1\",\n    \"tailwindcss\": \"^3.4.17\",\n    \"uuid\": \"^11.1.0\",\n    \"zustand\": \"^5.0.6\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.21.0\",\n    \"@types/node\": \"^24.0.10\",\n    \"@types/react\": \"^19.0.10\",\n    \"@types/react-dom\": \"^19.0.4\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"eslint\": \"^9.21.0\",\n    \"eslint-plugin-react-hooks\": \"^5.1.0\",\n    \"eslint-plugin-react-refresh\": \"^0.4.19\",\n    \"globals\": \"^15.15.0\",\n    \"typescript\": \"~5.7.2\",\n    \"typescript-eslint\": \"^8.24.1\",\n    \"vite\": \"^6.2.0\"\n  }\n}\n"
  },
  {
    "path": "pos/postcss.config.js",
    "content": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n} "
  },
  {
    "path": "pos/privateKey.js",
    "content": "export const privateKey = `PASTE_YOUR_PRIVATE_KEY_HERE`;"
  },
  {
    "path": "pos/src/App.tsx",
    "content": "import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';\nimport Footer from './components/Footer';\nimport Header from './components/Header';\nimport Orders from './pages/Orders';\nimport POS from './pages/POS';\nimport Table from './pages/Table';\nimport AuthGuard from './components/AuthGuard';\nimport POSOpeningProvider from './components/POSOpeningProvider';\nimport ScreenSizeProvider from './components/ScreenSizeProvider';\nimport { ToastProvider } from './components/ui/toast';\nimport { usePOSStore } from './store/pos-store';\nimport { useEffect } from 'react';\nimport { getActiveLanguage } from './i18n';\n\nfunction App() {\n  const {\n    initializeApp\n  } = usePOSStore();\n  \n  useEffect(() => {\n    initializeApp();\n  }, [initializeApp]);\n\n  useEffect(() => {\n    const lang = getActiveLanguage();\n    const isRtl = ['ar', 'he', 'fa', 'ur', 'ku'].includes(lang);\n    document.documentElement.dir = isRtl ? 'rtl' : 'ltr';\n    document.documentElement.lang = lang || 'en';\n  }, []);\n  return (\n    <>\n      <ToastProvider />\n      <ScreenSizeProvider>\n        <AuthGuard>\n          <POSOpeningProvider>\n            <Router basename=\"/pos\">\n              <div className=\"flex flex-col h-screen bg-gray-100 font-inter\">\n                <Header />\n                <div className=\"flex-1 overflow-hidden\">\n                  <Routes>\n                    <Route path=\"/\" element={<POS/>} />\n                    <Route path=\"/orders\" element={<Orders />} />\n                    <Route path=\"/table\" element={<Table />} />\n                  </Routes>\n                </div>\n                <Footer />\n              </div>\n            </Router>\n          </POSOpeningProvider>\n        </AuthGuard>\n      </ScreenSizeProvider>\n    </>\n  );\n}\n\nexport default App;\n"
  },
  {
    "path": "pos/src/components/AggregatorSelect.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { usePOSStore } from '../store/pos-store';\nimport { Select, SelectItem } from './ui/select';\nimport { getAggregators, type Aggregator } from '../lib/aggregator-api';\n\ninterface AggregatorSelectProps {\n  disabled?: boolean;\n}\n\nexport function AggregatorSelect({ disabled }: AggregatorSelectProps) {\n  const { selectedAggregator, setSelectedAggregator, fetchAggregatorMenu } = usePOSStore();\n  const [aggregators, setAggregators] = useState<Aggregator[]>([]);\n  const [loading, setLoading] = useState(false);\n\n  useEffect(() => {\n    const fetchAggregatorsList = async () => {\n      setLoading(true);\n      try {\n        const data = await getAggregators();\n        setAggregators(data);\n      } catch (error) {\n        console.error('Failed to fetch aggregators:', error);\n      } finally {\n        setLoading(false);\n      }\n    };\n\n    fetchAggregatorsList();\n  }, []);\n\n  const handleAggregatorChange = async (value: string) => {\n    const aggregator = aggregators.find(a => a.customer === value);\n    setSelectedAggregator(aggregator || null);\n    \n    if (aggregator) {\n      await fetchAggregatorMenu(aggregator.customer);\n    }\n  };\n\n  return (\n    <div>\n      <Select\n        value={selectedAggregator?.customer || ''}\n        onValueChange={handleAggregatorChange}\n        disabled={disabled || loading}\n        placeholder={loading ? 'Loading aggregators...' : 'Select an aggregator'}\n      >\n        {aggregators.map((aggregator) => (\n          <SelectItem \n            key={aggregator.customer} \n            value={aggregator.customer}\n            className=\"capitalize\"\n          >\n            {aggregator.customer}\n          </SelectItem>\n        ))}\n      </Select>\n    </div>\n  );\n} "
  },
  {
    "path": "pos/src/components/AuthGuard.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { useRootStore } from '../store/root-store';\nimport { Button } from './ui/button';\nimport { Spinner } from './ui/spinner';\nimport { RefreshCw } from 'lucide-react';\n\ninterface Props {\n  children: React.ReactNode;\n}\n\nconst AuthGuard: React.FC<Props> = ({ children }) => {\n  const { \n    checkAuth, \n    user, \n    isLoading: authLoading, \n    error: authError,\n    fetchPosProfile,\n    posProfile,\n    isLoading: configLoading,\n    error: configError,\n    hasAccess,\n  } = useRootStore();\n\n  // State to track if we're rechecking permissions\n  const [isRechecking, setIsRechecking] = useState(false);\n\n  useEffect(() => {\n    // Start auth check\n    checkAuth();\n  }, [checkAuth]);\n\n  // Once we have a user, fetch POS profile\n  useEffect(() => {\n    if (user) {\n      fetchPosProfile();\n    }\n  }, [user, fetchPosProfile]);\n\n  // Show loading state while either auth or config is loading\n  if (authLoading || (user && configLoading) || isRechecking) {\n    return (\n      <div className=\"min-h-screen\">\n        <Spinner />\n      </div>\n    );\n  }\n\n  if (authError || configError) {\n    return (\n      <div className=\"flex items-center justify-center min-h-screen\">\n        <div className=\"text-center\">\n          <div className=\"text-red-600 text-xl mb-4\">⚠️</div>\n          <h2 className=\"text-xl font-semibold text-gray-800 mb-2\">Access Denied</h2>\n          <p className=\"text-gray-600\">{authError || configError}</p>\n        </div>\n      </div>\n    );\n  }\n\n  if (!user) {\n    // The checkAuth function will handle the redirect to login\n    return null;\n  }\n\n  if (!posProfile) {\n    return (\n      <div className=\"flex items-center justify-center min-h-screen\">\n        <div className=\"text-center\">\n          <div className=\"text-amber-600 text-xl mb-4\">⚠️</div>\n          <h2 className=\"text-xl font-semibold text-gray-800 mb-2\">Configuration Error</h2>\n          <p className=\"text-gray-600\">POS Profile not found or not configured.</p>\n        </div>\n      </div>\n    );\n  }\n\n  if (!hasAccess) {\n    return (\n      <div className=\"flex items-center justify-center min-h-screen\">\n        <div className=\"text-center\">\n          <div className=\"text-amber-600 text-xl mb-4\">🔒</div>\n          <h2 className=\"text-xl font-semibold text-gray-800 mb-2\">Permission Required</h2>\n          <p className=\"text-gray-600\">You do not have permission to access this application.</p>\n          <p className=\"text-sm text-gray-500 mt-2\">Required roles: {posProfile.role_allowed_for_billing.map(r => r.role).join(', ')}</p>\n          <Button \n            variant=\"outline\"\n            className=\"mt-4\"\n            onClick={async () => {\n              setIsRechecking(true);\n              try {\n                await fetchPosProfile(true); // Force refresh the POS profile\n              } finally {\n                setIsRechecking(false);\n              }\n            }}\n          >\n            <RefreshCw className=\"w-4 h-4 mr-2\" />\n            Recheck Permissions\n          </Button>\n        </div>\n      </div>\n    );\n  }\n\n  return <>{children}</>;\n};\n\nexport default AuthGuard; "
  },
  {
    "path": "pos/src/components/CommentDialog.tsx",
    "content": "import { useState } from 'react';\nimport { MessageSquare, X } from 'lucide-react';\nimport { Button } from './ui';\nimport { t } from '../i18n';\n\ninterface CommentDialogProps {\n  isOpen: boolean;\n  onClose: () => void;\n  onSave: (comment: string) => void;\n  initialComment?: string;\n}\n\nconst CommentDialog = ({ isOpen, onClose, onSave, initialComment = '' }: CommentDialogProps) => {\n  const [comment, setComment] = useState(initialComment);\n\n  const handleSave = () => {\n    onSave(comment);\n    onClose();\n  };\n\n  const handleCancel = () => {\n    setComment(initialComment);\n    onClose();\n  };\n\n  if (!isOpen) return null;\n\n  return (\n    <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\n      <div className=\"bg-white rounded-lg p-6 max-w-md w-full mx-4 shadow-xl\">\n        <div className=\"flex items-center justify-between mb-4\">\n          <div className=\"flex items-center gap-2\">\n            <MessageSquare className=\"w-5 h-5 text-blue-600\" />\n            <h2 className=\"text-lg font-semibold text-gray-900\">\n              {t('comment.title')}\n            </h2>\n          </div>\n          <Button\n            onClick={handleCancel}\n            variant=\"ghost\"\n            size=\"sm\"\n            className=\"h-8 w-8 p-0\"\n          >\n            <X className=\"w-4 h-4\" />\n          </Button>\n        </div>\n        \n        <div className=\"mb-6\">\n          <label htmlFor=\"comment\" className=\"block text-sm font-medium text-gray-700 mb-2\">\n            {t('comment.label')}\n          </label>\n          <textarea\n            id=\"comment\"\n            value={comment}\n            onChange={(e) => setComment(e.target.value)}\n            placeholder={t('comment.placeholder')}\n            className=\"w-full h-32 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none\"\n            autoFocus\n          />\n        </div>\n        \n        <div className=\"flex gap-3 justify-end\">\n          <Button\n            onClick={handleCancel}\n            variant=\"outline\"\n            className=\"px-4 py-2\"\n          >\n            {t('common.cancel')}\n          </Button>\n          <Button\n            onClick={handleSave}\n            className=\"px-4 py-2 bg-blue-600 hover:bg-blue-700\"\n          >\n            {t('comment.save_button')}\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default CommentDialog; "
  },
  {
    "path": "pos/src/components/CustomerSelect.tsx",
    "content": "import { useState, useRef, useEffect } from 'react';\nimport { UserPlus, Mail, Phone, Loader } from 'lucide-react';\nimport { usePOSStore, type Customer } from '../store/pos-store';\nimport { Button, Dialog, DialogContent, Input } from './ui';\nimport { Select, SelectItem } from './ui';\nimport { ChevronDown } from 'lucide-react';\nimport React from 'react';\nimport { addCustomer, type CreateCustomerData, searchCustomers } from '../lib/customer-api';\nimport { AggregatorSelect } from './AggregatorSelect';\nimport { t } from '../i18n';\n\n// NewCustomerForm component\nfunction NewCustomerForm({ \n  onClose, \n  onSuccess, \n  isCreatingCustomer: parentIsCreatingCustomer, \n  setIsCreatingCustomer: setParentIsCreatingCustomer, \n  prefillName = '',\n  prefillPhone = ''\n}: { \n  onClose: () => void; \n  onSuccess?: () => void;\n  isCreatingCustomer?: boolean;\n  setIsCreatingCustomer?: React.Dispatch<React.SetStateAction<boolean>>;\n  prefillName?: string;\n  prefillPhone?: string;\n}) {\n  const { customerGroups, territories, fetchCustomerGroups, fetchTerritories, setSelectedCustomer } = usePOSStore();\n  const [newCustomerName, setNewCustomerName] = React.useState('');\n  const [newCustomerPhone, setNewCustomerPhone] = React.useState('');\n  const [newCustomerGroup, setNewCustomerGroup] = React.useState(\"\");\n  const [newCustomerTerritory, setNewCustomerTerritory] = React.useState(\"\");\n  const [formError, setFormError] = React.useState(false);\n  const [apiError, setApiError] = React.useState<string>(\"\");\n  const [loadingGroups, setLoadingGroups] = React.useState(false);\n  const [loadingTerritories, setLoadingTerritories] = React.useState(false);\n  \n  // Use parent loading state if available, otherwise fallback to local state\n  const [localIsCreatingCustomer, setLocalIsCreatingCustomer] = React.useState(false);\n  const isCreatingCustomer = parentIsCreatingCustomer ?? localIsCreatingCustomer;\n  const setIsCreatingCustomer = setParentIsCreatingCustomer ?? setLocalIsCreatingCustomer;\n\n  // Handle prefill values\n  React.useEffect(() => {\n    if (prefillName) {\n      setNewCustomerName(prefillName);\n    }\n    if (prefillPhone) {\n      setNewCustomerPhone(prefillPhone);\n    }\n  }, [prefillName, prefillPhone]);\n\n  // Fetch groups/territories on mount\n  React.useEffect(() => {\n    if (!customerGroups.length) {\n      setLoadingGroups(true);\n      fetchCustomerGroups().finally(() => setLoadingGroups(false));\n    }\n    if (!territories.length) {\n      setLoadingTerritories(true);\n      fetchTerritories().finally(() => setLoadingTerritories(false));\n    }\n  }, []);\n\n\n\n  async function handleAddCustomerSubmit(e: React.FormEvent<HTMLFormElement>) {\n    e.preventDefault();\n    if (!newCustomerName || !newCustomerPhone) {\n      setFormError(true);\n      return;\n    }\n\n    setFormError(false);\n    setApiError(\"\");\n    setIsCreatingCustomer(true);\n\n    try {\n      const customerData: CreateCustomerData = {\n        customer_name: newCustomerName.trim(),\n        mobile_number: newCustomerPhone.trim(),\n      };\n\n      // Add optional fields only if they have values\n      if (newCustomerGroup) {\n        customerData.customer_group = newCustomerGroup;\n      }\n      if (newCustomerTerritory) {\n        customerData.territory = newCustomerTerritory;\n      }\n\n      const response = await addCustomer(customerData);\n      const created = response.data;\n      // Set selected customer in POS store\n      setSelectedCustomer({\n        id: created.name,\n        name: created.customer_name,\n        phone: created.mobile_number,\n      });\n      // Reset form on success\n      setNewCustomerName(\"\");\n      setNewCustomerPhone(\"\");\n      setNewCustomerGroup(\"\");\n      setNewCustomerTerritory(\"\");\n      if (onSuccess) onSuccess();\n      onClose();\n    } catch (error: any) {\n      console.error('Failed to create customer:', error);\n      setApiError(error?.message || t('customer.failed_create'));\n    } finally {\n      setIsCreatingCustomer(false);\n    }\n  }\n\n  return (\n    <form className=\"space-y-4\" onSubmit={handleAddCustomerSubmit}>\n      {apiError && (\n        <div className=\"bg-red-50 border border-red-200 rounded-md p-3\">\n          <div className=\"text-sm text-red-600\">{apiError}</div>\n        </div>\n      )}\n      <div>\n        <label className=\"block text-sm font-medium text-gray-700 mb-1\" htmlFor=\"new-customer-name\">{t('customer.name_label')} <span className=\"text-red-500\">*</span></label>\n        <Input\n          id=\"new-customer-name\"\n          type=\"text\"\n          value={newCustomerName}\n          onChange={e => setNewCustomerName(e.target.value)}\n          required\n          disabled={isCreatingCustomer}\n          aria-invalid={!!formError && !newCustomerName}\n        />\n        {formError && !newCustomerName && (\n          <div className=\"text-xs text-red-500 mt-1\">{t('customer.name_required')}</div>\n        )}\n      </div>\n      <div>\n        <label className=\"block text-sm font-medium text-gray-700 mb-1\" htmlFor=\"new-customer-phone\">{t('customer.phone_label')} <span className=\"text-red-500\">*</span></label>\n        <div className=\"relative\">\n          <Input\n            id=\"new-customer-phone\"\n            type=\"tel\"\n            value={newCustomerPhone}\n            onChange={e => setNewCustomerPhone(e.target.value)}\n            required\n            disabled={isCreatingCustomer}\n            className=\"pl-10\"\n            aria-invalid={!!formError && !newCustomerPhone}\n          />\n          <Phone className=\"absolute left-3 top-2.5 text-gray-400 w-5 h-5\" />\n        </div>\n        {formError && !newCustomerPhone && (\n          <div className=\"text-xs text-red-500 mt-1\">{t('customer.phone_required')}</div>\n        )}\n      </div>\n      <div>\n        <label className=\"block text-sm font-medium text-gray-700 mb-1\">{t('customer.customer_group_label')}</label>\n        <Select\n          placeholder={loadingGroups ? t('common.loading') : t('customer.select_group')}\n          value={newCustomerGroup}\n          onValueChange={setNewCustomerGroup}\n          disabled={isCreatingCustomer || loadingGroups || !customerGroups.length}\n        >\n          {customerGroups.map((group) => (\n            <SelectItem key={group} value={group} className=\"capitalize\">\n              {group}\n            </SelectItem>\n          ))}\n        </Select>\n        {!loadingGroups && !customerGroups.length && (\n          <div className=\"text-xs text-gray-400 mt-1\">{t('common.no_options')}</div>\n        )}\n      </div>\n      <div>\n        <label className=\"block text-sm font-medium text-gray-700 mb-1\">{t('customer.territory_label')}</label>\n        <Select\n          placeholder={loadingTerritories ? t('common.loading') : t('customer.select_territory')}\n          value={newCustomerTerritory}\n          onValueChange={setNewCustomerTerritory}\n          disabled={isCreatingCustomer || loadingTerritories || !territories.length}\n        >\n          {territories.map((territory) => (\n            <SelectItem key={territory} value={territory} className=\"capitalize\">\n              {territory}\n            </SelectItem>\n          ))}\n        </Select>\n        {!loadingTerritories && !territories.length && (\n          <div className=\"text-xs text-gray-400 mt-1\">{t('common.no_options')}</div>\n        )}\n      </div>\n      <div className=\"flex gap-3 mt-6\">\n        <Button\n          type=\"submit\"\n          variant=\"default\"\n          className=\"flex-1\"\n          disabled={isCreatingCustomer}\n        >\n          {isCreatingCustomer ? (\n            <>\n              <Loader className=\"w-4 h-4 mr-2 animate-spin\" />\n              {t('customer.adding')}\n            </>\n          ) : (\n            t('customer.add_button')\n          )}\n        </Button>\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          onClick={onClose}\n          disabled={isCreatingCustomer}\n        >\n          {t('common.cancel')}\n        </Button>\n      </div>\n    </form>\n  );\n}\n\ninterface CustomerSelectProps {\n  disabled?: boolean;\n}\n\nexport function CustomerSelect({ disabled }: CustomerSelectProps) {\n  const { selectedCustomer, setSelectedCustomer, selectedOrderType, isUpdatingOrder } = usePOSStore();\n  const [showNewCustomerForm, setShowNewCustomerForm] = useState(false);\n  const [isCreatingCustomer, setIsCreatingCustomer] = useState(false);\n  const [searchTerm, setSearchTerm] = useState('');\n  const [isOpen, setIsOpen] = useState(false);\n  const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);\n  const [searchResults, setSearchResults] = useState<any[]>([]);\n  const [isSearching, setIsSearching] = useState(false);\n  const [searchError, setSearchError] = useState<string | null>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const [prefillName, setPrefillName] = useState('');\n  const [prefillPhone, setPrefillPhone] = useState('');\n\n  // Debounced search\n  useEffect(() => {\n    if (!isOpen || !searchTerm.trim()) {\n      setSearchResults([]);\n      setSearchError(null);\n      setIsSearching(false);\n      return;\n    }\n    setIsSearching(true);\n    setSearchError(null);\n    const handler = setTimeout(() => {\n      searchCustomers(searchTerm)\n        .then(results => {\n          setSearchResults(results);\n          setIsSearching(false);\n        })\n        .catch(err => {\n          setSearchError(t('customer.failed_search'));\n          setIsSearching(false);\n        });\n    }, 300);\n    return () => clearTimeout(handler);\n  }, [searchTerm, isOpen]);\n\n  // Handle keyboard navigation\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    if (!isOpen && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {\n      setIsOpen(true);\n      setHighlightedIndex(0);\n      return;\n    }\n    if (e.key === 'ArrowDown') {\n      setHighlightedIndex((prev) => Math.min(prev + 1, searchResults.length));\n      e.preventDefault();\n    } else if (e.key === 'ArrowUp') {\n      setHighlightedIndex((prev) => Math.max(prev - 1, 0));\n      e.preventDefault();\n    } else if (e.key === 'Enter') {\n      if (isOpen) {\n        if (highlightedIndex === searchResults.length) {\n          setShowNewCustomerForm(true);\n          setIsOpen(false);\n        } else if (searchResults[highlightedIndex]) {\n          // The API returns { name, content, ... }\n          const customer = searchResults[highlightedIndex];\n          setSelectedCustomer({\n            id: customer.name,\n            name: customer.content?.match(/Customer Name : ([^|]+)/)?.[1]?.trim() || customer.name,\n            phone: customer.content?.match(/Mobile Number : ([^|]+)/)?.[1]?.trim() || '',\n          });\n          setSearchTerm('');\n          setIsOpen(false);\n        }\n      }\n    } else if (e.key === 'Escape') {\n      setIsOpen(false);\n    }\n  };\n\n  if (selectedOrderType === 'Aggregators') {\n    return <AggregatorSelect />;\n  }\n\n  return (\n    <div className=\"relative\">\n      {selectedCustomer ? (\n        <div className=\"flex items-center justify-between bg-blue-50 p-3 rounded-lg\">\n          <div>\n            <p className=\"font-medium text-blue-900\">{selectedCustomer.name}</p>\n            <p className=\"text-sm text-blue-700\">{selectedCustomer.phone}</p>\n          </div>\n          <Button\n            onClick={() => setSelectedCustomer(null)}\n            disabled={isUpdatingOrder}\n            variant=\"ghost\"\n            size=\"sm\"\n            className=\"text-blue-700 hover:text-blue-800\"\n          >\n            {t('common.change')}\n          </Button>\n        </div>\n      ) : (\n        <div className=\"relative\">\n          <div className=\"flex items-center relative\">\n            <input\n              ref={inputRef}\n              type=\"text\"\n              value={searchTerm}\n              onChange={e => {\n                setSearchTerm(e.target.value);\n                setIsOpen(true);\n                setHighlightedIndex(0);\n              }}\n              onFocus={() => setIsOpen(true)}\n              onBlur={e => {\n                setTimeout(() => setIsOpen(false), 100);\n              }}\n              onKeyDown={handleKeyDown}\n              placeholder={t('customer.search_placeholder')}\n              className=\"w-full h-10 border border-gray-200 rounded-lg px-4 py-2 text-sm font-medium text-gray-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 transition-colors\"\n              aria-label={t('customer.search_placeholder')}\n              autoComplete=\"off\"\n            />\n            <ChevronDown className=\"absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none\" />\n          </div>\n          {isOpen && (\n            <div className=\"absolute w-full mt-2 bg-white border border-gray-200 rounded-lg shadow-lg z-50 max-h-80 overflow-y-auto\">\n              {searchTerm.trim() === '' && !isSearching && !searchError && (\n                <div className=\"p-4 text-center text-gray-400 text-sm select-none\">{t('customer.type_to_search')}</div>\n              )}\n              {isSearching && (\n                <div className=\"flex items-center justify-center p-4 text-gray-500 text-sm select-none\">\n                  <Loader className=\"w-4 h-4 mr-2 animate-spin\" /> {t('common.searching')}\n                </div>\n              )}\n              {searchError && (\n                <div className=\"p-4 text-center text-red-500 text-sm select-none\">{searchError}</div>\n              )}\n              {!isSearching && !searchError && searchResults.length > 0 && searchResults.map((customer, idx) => {\n                const name = customer.content?.match(/Customer Name : ([^|]+)/)?.[1]?.trim() || customer.name;\n                const phone = customer.content?.match(/Mobile Number : ([^|]+)/)?.[1]?.trim() || '';\n                return (\n                  <button\n                    key={customer.name}\n                    type=\"button\"\n                    className={`w-full gap-2 px-4 py-2 text-left rounded-md text-gray-800 text-sm select-none transition-colors ${\n                      idx === highlightedIndex ? 'bg-primary-50 text-primary-700' : 'hover:bg-gray-50'\n                    }`}\n                    onMouseDown={() => {\n                      setSelectedCustomer({ id: customer.name, name, phone });\n                      setSearchTerm('');\n                      setIsOpen(false);\n                    }}\n                    onMouseEnter={() => setHighlightedIndex(idx)}\n                  >\n                    <div className=\"font-medium\">{name}</div>\n                    <div className=\"ml-auto text-xs text-gray-500\">{phone}</div>\n                  </button>\n                );\n              })}\n              {!isSearching && !searchError && searchResults.length === 0 && searchTerm.trim() && (\n                <div className=\"p-4 text-center text-gray-400 text-sm select-none\">{t('customer.no_customers_found')}</div>\n              )}\n              <div className=\"my-1 h-px bg-gray-100\" />\n              <button\n                type=\"button\"\n                className={`flex items-center gap-2 w-full px-4 py-2 text-primary-600 hover:text-primary-700 hover:bg-gray-50 font-medium rounded-md text-sm select-none transition-colors ${\n                  highlightedIndex === searchResults.length ? 'bg-primary-50' : ''\n                }`}\n                onMouseDown={() => {\n                  // Prefill logic\n                  if (/^\\d+$/.test(searchTerm.trim())) {\n                    setPrefillPhone(searchTerm.trim());\n                    setPrefillName('');\n                  } else {\n                    setPrefillName(searchTerm.trim());\n                    setPrefillPhone('');\n                  }\n                  setShowNewCustomerForm(true);\n                  setIsOpen(false);\n                }}\n                onMouseEnter={() => setHighlightedIndex(searchResults.length)}\n              >\n                <UserPlus className=\"w-4 h-4\" /> {searchTerm.trim() ? t('customer.add_with_name', { name: searchTerm.trim() }) : t('customer.add_new')}\n              </button>\n            </div>\n          )}\n        </div>\n      )}\n      {showNewCustomerForm && (\n        <Dialog \n          open={showNewCustomerForm} \n          onOpenChange={(open) => {\n            // Prevent closing the dialog when creating customer\n            if (!isCreatingCustomer) {\n              setShowNewCustomerForm(open);\n            }\n          }}\n        >\n          <DialogContent className=\"w-full max-w-md p-4 max-h-[80vh] overflow-y-auto\">\n            <h3 className=\"text-lg font-semibold text-gray-900 mb-4\">{t('customer.add_customer_title')}</h3>\n            <NewCustomerForm \n              onClose={() => setShowNewCustomerForm(false)} \n              isCreatingCustomer={isCreatingCustomer}\n              setIsCreatingCustomer={setIsCreatingCustomer}\n              prefillName={prefillName}\n              prefillPhone={prefillPhone}\n            />\n          </DialogContent>\n        </Dialog>\n      )}\n    </div>\n  );\n} "
  },
  {
    "path": "pos/src/components/Footer.tsx",
    "content": "import { NavLink } from 'react-router-dom';\nimport { \n  LayoutGrid, \n  ClipboardList, \n  Table,\n} from 'lucide-react';\nimport { cn } from '../lib/utils';\nimport { t } from '../i18n';\n\nconst Footer = () => {\n\n  const navItems = [\n    { icon: LayoutGrid, label: t('footer.pos'), path: '/' },\n    {icon: Table, label: t('footer.table'), path: '/table'},\n    { icon: ClipboardList, label: t('footer.orders'), path: '/orders' },\n  ];\n\n  return (\n    <div className=\"bg-white border-t border-gray-200 py-2 relative\">\n      <nav className=\"max-w-screen-xl mx-auto px-4\">\n        <div className=\"flex justify-center items-center gap-4\">\n          {navItems.map((item) => (\n            <NavLink\n              key={item.path}\n              to={item.path}\n              className={({ isActive }) =>\n                cn(\n                  'flex flex-col items-center p-2 rounded-lg text-gray-700 hover:bg-gray-100 transition-colors',\n                  isActive && 'text-blue-600'\n                )\n              }\n            >\n              <item.icon className=\"w-5 h-5\" />\n              <span className=\"text-xs mt-1\">{item.label}</span>\n            </NavLink>\n          ))}\n        </div>\n      </nav>\n    </div>\n  );\n};\n\nexport default Footer; "
  },
  {
    "path": "pos/src/components/Header.tsx",
    "content": "import { useState, useEffect, useRef } from 'react';\nimport { t } from '../i18n';\nimport { Link, useLocation } from 'react-router-dom';\nimport { \n  Command,\n  User,\n  ChevronDown,\n  Monitor,\n  LogOut,\n  RefreshCw,\n} from 'lucide-react';\nimport { Button, Input } from './ui';\nimport { useRootStore } from '../store/root-store';\nimport { usePOSStore } from '../store/pos-store';\nimport type { RootState } from '../store/root-store';\nimport { logout } from '../lib/auth-api';\nimport { showToast } from './ui/toast';\n\nconst Header = () => {\n  const [showUserMenu, setShowUserMenu] = useState(false);\n  const userMenuRef = useRef<HTMLDivElement>(null);\n  const user = useRootStore((state: RootState) => state.user);\n  const searchInputRef = useRef<HTMLInputElement>(null);\n  const location = useLocation();\n  const { searchQuery, setSearchQuery } = usePOSStore();\n  const { orderSearchQuery, setOrderSearchQuery } = useRootStore();\n  const [orderSearchInput, setOrderSearchInput] = useState(orderSearchQuery);\n\n  // Determine placeholder and handlers based on route\n  let searchPlaceholder = t('header.search_placeholder_default');\n  let searchValue: string | undefined = undefined;\n  let searchOnChange: ((e: React.ChangeEvent<HTMLInputElement>) => void) | undefined = undefined;\n  if (location.pathname === '/orders') {\n    searchPlaceholder = t('header.search_placeholder_orders');\n    searchValue = orderSearchInput;\n    searchOnChange = (e) => setOrderSearchInput(e.target.value);\n  } else if (location.pathname === '/') {\n    searchPlaceholder = t('header.search_placeholder_menu');\n    searchValue = searchQuery;\n    searchOnChange = (e) => setSearchQuery(e.target.value);\n  }\n\n  // Debounce order search\n  useEffect(() => {\n    if (location.pathname !== '/orders') return;\n    const handler = setTimeout(() => {\n      setOrderSearchQuery(orderSearchInput);\n    }, 300);\n    return () => clearTimeout(handler);\n  }, [orderSearchInput, setOrderSearchQuery, location.pathname]);\n\n  // Keep input in sync with store (if cleared elsewhere)\n  useEffect(() => {\n    if (location.pathname === '/orders') {\n      setOrderSearchInput(orderSearchQuery);\n    }\n  }, [location.pathname, orderSearchQuery]);\n\n  // Handle clicks outside of menus\n  useEffect(() => {\n    const handleClickOutside = (event: MouseEvent) => {\n      if (userMenuRef.current && !userMenuRef.current.contains(event.target as Node)) {\n        setShowUserMenu(false);\n      }\n    };\n\n    document.addEventListener('mousedown', handleClickOutside);\n    return () => {\n      document.removeEventListener('mousedown', handleClickOutside);\n    };\n  }, []);\n\n  useEffect(() => {\n    const handleKeyDown = (e: KeyboardEvent) => {\n      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {\n        e.preventDefault();\n        searchInputRef.current?.focus();\n      }\n    };\n    document.addEventListener('keydown', handleKeyDown);\n    return () => document.removeEventListener('keydown', handleKeyDown);\n  }, []);\n\n  const handleUserMenuToggle = () => {\n    setShowUserMenu(!showUserMenu);\n  };\n\n  const handleLogout = async () => {\n    try {\n      await logout();\n      window.location.href = '/login?redirect-to=%2Fpos';\n    } catch (error) {\n      showToast.error(t('errors.failed_logout'));\n    }\n  };\n\n  const handleClearCache = () => {\n    // Clear all local storage\n    localStorage.clear();\n    // Clear all session storage\n    sessionStorage.clear();\n    // Reload the page\n    window.location.reload();\n  };\n\n  return (\n    <header className=\"bg-white border-b border-gray-200\">\n      <div className=\"flex items-center justify-between h-16 px-6\">\n        {/* Logo */}\n        <div className=\"flex items-center\">\n        <Link to=\"/\" className=\"flex items-center gap-3\">\n            <img \n              src=\"/assets/ury/pos/ury_pos.png\" \n              alt=\"URY POS\" \n              className=\"h-10 w-auto\"\n            />\n          </Link>\n        </div>\n\n        {/* Search Bar */}\n        <div className=\"px-4 py-2 flex-1 flex items-center max-w-2xl mx-8  bg-gray-50 hover:bg-gray-100 border border-input rounded-md\">\n            <Input\n              ref={searchInputRef}\n              placeholder={searchPlaceholder}\n              className=\"h-fit p-0 w-full bg-transparent border-0 focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0\"\n              value={searchValue}\n              onChange={searchOnChange}\n            />\n            <div className=\"flex items-center gap-2 text-gray-400\">\n              <Command className=\"w-4 h-4\" />\n              <span>K</span>\n            </div>\n        </div>\n\n        {/* Right side actions */}\n        <div className=\"flex items-center gap-4\">\n          {/* User menu */}\n          <div className=\"relative\" ref={userMenuRef}>\n            <Button\n              onClick={handleUserMenuToggle}\n              variant=\"ghost\"\n              className=\"flex items-center gap-2 text-gray-600 hover:text-gray-900\"\n            >\n              <div className=\"w-8 h-8 bg-primary-500 rounded-full flex items-center justify-center\">\n                <User className=\"w-4 h-4 text-white\" />\n              </div>\n              <span className=\"text-sm font-medium\">{user?.full_name || 'User'}</span>\n              <ChevronDown className=\"w-4 h-4\" />\n            </Button>\n\n            {/* User dropdown */}\n            {showUserMenu && (\n              <div className=\"absolute end-0 mt-2 w-56 bg-white rounded-lg shadow-lg border border-gray-200 z-50\">\n                <div className=\"p-4 border-b border-gray-200\">\n                  <p className=\"text-sm font-medium text-gray-900\">{user?.full_name || 'User'}</p>\n                  <p className=\"text-sm text-gray-500\">{user?.name || ''}</p>\n                </div>\n                <div className=\"py-2\">\n                  <Button\n                    variant=\"ghost\"\n                    className=\"flex justify-start items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors\"\n                    onClick={() => window.location.href = '/app'}\n                  >\n                    <Monitor className=\"w-4 h-4 me-3\" />\n                    {t('header.switch_to_desk')}\n                  </Button>\n                  <Button\n                    variant=\"ghost\"\n                    className=\"flex justify-start items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors\"\n                    onClick={handleClearCache}\n                  >\n                    <RefreshCw className=\"w-4 h-4 me-3\" />\n                    {t('header.clear_cache')}\n                  </Button>\n                  <Button\n                    variant=\"ghost\"\n                    className=\"flex justify-start items-center w-full px-4 py-2 text-sm text-red-600 hover:bg-red-50 hover:text-red-700 transition-colors\"\n                    onClick={handleLogout}\n                  >\n                    <LogOut className=\"w-4 h-4 me-3\" />\n                    {t('header.logout')}\n                  </Button>\n                </div>\n              </div>\n            )}\n          </div>\n        </div>\n      </div>\n    </header>\n  );\n};\n\nexport default Header; "
  },
  {
    "path": "pos/src/components/InitialLoader.tsx",
    "content": "import React from 'react';\nimport { Spinner } from './ui/spinner';\nimport { t } from '../i18n';\n\nconst InitialLoader: React.FC = () => {\n  return (\n    <div className=\"fixed inset-0 bg-white flex items-center justify-center\">\n      <div className=\"text-center\">\n        <Spinner className=\"w-12 h-12\" />\n        <p className=\"mt-4 text-lg font-medium text-gray-900\">{t('common.loading_ury_pos')}</p>\n        <p className=\"mt-2 text-sm text-gray-500\">{t('common.please_wait_setup')}</p>\n      </div>\n    </div>\n  );\n};\n\nexport default InitialLoader; "
  },
  {
    "path": "pos/src/components/LayoutView.tsx",
    "content": "import React, { useState, useRef, useMemo, useEffect, useCallback } from 'react';\nimport { CreditCard as Edit3, Save, Users, Move, X, Grid3x3 as Grid3X3, ZoomIn, ZoomOut, RotateCcw } from 'lucide-react';\nimport { cn, formatInvoiceTime } from '../lib/utils';\nimport { Table, updateTableLayout } from '../lib/table-api';\nimport { getTableOrder, POSInvoice } from '../lib/order-api';\nimport { Button } from './ui';\nimport { t } from '../i18n';\n\n\n\ninterface Props {\n  selectedRoom: string;\n  tables: Table[];\n  onBackToGrid: () => void;\n  onRefresh?: () => void; // Add refresh callback\n}\n\nconst LayoutView: React.FC<Props> = ({ selectedRoom, tables, onBackToGrid, onRefresh }) => {\n  const isRTL = document.dir === 'rtl';\n  const [isEditMode, setIsEditMode] = useState(false);\n\n  // Local state for optimistic updates\n  const [localLayouts, setLocalLayouts] = useState<Record<string, Partial<Table>>>({});\n  const [draggedTable, setDraggedTable] = useState<string | null>(null);\n  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });\n  const [selectedTable, setSelectedTable] = useState<string | null>(null);\n  const [selectedTableOrder, setSelectedTableOrder] = useState<POSInvoice | null>(null);\n  const canvasRef = useRef<HTMLDivElement>(null);\n  const [zoom, setZoom] = useState(1);\n  const [panOffset, setPanOffset] = useState({ x: 0, y: 0 });\n  const [isPanning, setIsPanning] = useState(false);\n  const [panStart, setPanStart] = useState({ x: 0, y: 0 });\n  const [capacityInput, setCapacityInput] = useState<string>('');\n\n  // Save to local storage effect removed\n\n\n  // Merge props.tables with saved positions\n  const tablesWithPosition = useMemo(() => {\n    return tables.map((table, index) => {\n      const local = localLayouts[table.name] || {};\n\n      // Use local overrides, then backend fields, then grid defaults\n      const x = local.layout_x ?? table.layout_x ?? (100 + (index % 5) * 150);\n      const y = local.layout_y ?? table.layout_y ?? (100 + Math.floor(index / 5) * 150);\n\n      return {\n        ...table,\n        x,\n        y,\n        table_shape: local.table_shape ?? table.table_shape,\n        no_of_seats: local.no_of_seats ?? table.no_of_seats,\n      };\n    });\n  }, [tables, localLayouts]);\n\n  // Sync capacity input when selected table changes\n  useEffect(() => {\n    if (selectedTable) {\n      const table = tablesWithPosition.find(t => t.name === selectedTable);\n      setCapacityInput(table?.no_of_seats?.toString() ?? '');\n    }\n  }, [selectedTable, tablesWithPosition]);\n\n  // Calculate table dimensions based on capacity and shape\n  const getTableDimensions = (shape: string, capacity: number = 4) => {\n    // Dynamic sizing: minimum 60px, scales up by 10px per person, max 250px\n    const size = Math.max(60, Math.min(250, 60 + (capacity * 10)));\n\n    const normalizedShape = shape?.toLowerCase() || 'rectangle';\n\n    switch (normalizedShape) {\n      case 'circle':\n        return { width: size, height: size };\n      case 'square':\n        return { width: size, height: size };\n      case 'rectangle':\n      default:\n        return { width: size * 1.5, height: size };\n    }\n  };\n\n  // Zoom functionality (simple scale)\n  const handleZoomIn = () => setZoom(prev => Math.min(prev + 0.1, 3));\n  const handleZoomOut = () => setZoom(prev => Math.max(prev - 0.1, 0.3));\n  const handleResetZoom = () => {\n    setZoom(1);\n    setPanOffset({ x: 0, y: 0 });\n  };\n\n  // Mouse wheel zoom (simple scale)\n  const handleWheel = useCallback((e: WheelEvent) => {\n    e.preventDefault();\n    e.stopPropagation();\n    const delta = e.deltaY > 0 ? -0.1 : 0.1;\n    setZoom(prev => Math.max(0.3, Math.min(3, prev + delta)));\n  }, []);\n\n  // Use ref to attach non-passive listener for proper preventDefault\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (canvas) {\n      canvas.addEventListener('wheel', handleWheel, { passive: false });\n    }\n    return () => {\n      if (canvas) {\n        canvas.removeEventListener('wheel', handleWheel);\n      }\n    };\n  }, [handleWheel]);\n\n  // Pan functionality\n  const handleCanvasMouseDown = (e: React.MouseEvent) => {\n    // Start panning if we clicked on the background (wrapper or outer container)\n    // Tables stop propagation, so if we get here, it's safe to pan\n    setIsPanning(true);\n    setPanStart({ x: e.clientX - panOffset.x, y: e.clientY - panOffset.y });\n  };\n\n  const handleCanvasMouseMove = (e: React.MouseEvent) => {\n    if (isPanning) {\n      setPanOffset({\n        x: e.clientX - panStart.x,\n        y: e.clientY - panStart.y\n      });\n      return;\n    }\n\n    // Drag functionality\n    if (!draggedTable || !isEditMode || !canvasRef.current) return;\n\n    const canvasRect = canvasRef.current.getBoundingClientRect();\n\n    // Classic drag math\n    const newX = (e.clientX - canvasRect.left) / zoom - dragOffset.x - panOffset.x / zoom;\n    const newY = (e.clientY - canvasRect.top) / zoom - dragOffset.y - panOffset.y / zoom;\n\n    // Update local state for immediate feedback\n    setLocalLayouts(prev => ({\n      ...prev,\n      [draggedTable]: {\n        ...(prev[draggedTable] || {}),\n        layout_x: newX,\n        layout_y: newY,\n      }\n    }));\n  };\n\n  const persistTableUpdate = (tableName: string, changes: Partial<Table>) => {\n    const table = tablesWithPosition.find(t => t.name === tableName);\n    if (!table) return Promise.reject(\"Table not found\");\n\n    // AND ensure we fallback to backend values for undefined fields.\n    const payload = {\n      layout_x: changes.layout_x ?? table.x,\n      layout_y: changes.layout_y ?? table.y,\n      table_shape: changes.table_shape ?? table.table_shape,\n      no_of_seats: changes.no_of_seats ?? table.no_of_seats,\n      minimum_seating: table.minimum_seating // preserve existing if not changing\n    };\n    return updateTableLayout(tableName, payload);\n  };\n\n  const handleCanvasMouseUp = () => {\n    if (draggedTable && isEditMode) {\n      const table = tablesWithPosition.find(t => t.name === draggedTable);\n      if (table) {\n        // table.x and table.y are already updated via local state during drag\n        persistTableUpdate(table.name, {\n          layout_x: table.x,\n          layout_y: table.y\n        }).catch(err => console.error(\"Failed to save layout\", err));\n      }\n    }\n\n    setIsPanning(false);\n    setDraggedTable(null);\n    setDragOffset({ x: 0, y: 0 });\n  };\n\n  const getTableStatusColor = (occupied: number) => {\n    return occupied\n      ? 'bg-amber-100 border-amber-300 text-amber-900 shadow-sm'\n      : 'bg-emerald-50 border-emerald-200 text-emerald-800 hover:shadow-md';\n  };\n\n  const handleMouseDown = (e: React.MouseEvent, table: typeof tablesWithPosition[0]) => {\n    e.stopPropagation();\n\n    setSelectedTable(table.name);\n\n    if (table.occupied) {\n      getTableOrder(table.name).then(res => {\n        setSelectedTableOrder(res.message);\n      }).catch(console.error);\n    } else {\n      setSelectedTableOrder(null);\n    }\n\n    if (!isEditMode) return;\n\n    const canvasRect = canvasRef.current?.getBoundingClientRect();\n    if (!canvasRect) return;\n\n    // Calculate offset for drag start\n    // We need to match the drag math: \n    // newX = (mouseX - rect.left)/zoom - dragOffset - pan/zoom\n    // So dragOffset = (mouseX - rect)/zoom - startX - pan/zoom\n\n    // Actually, just use standard offset from top-left of element?\n    // Wait, the newX formula sets the new TopLeft.\n    // So dragOffset should be the difference between Mouse and TableTopLeft (in scaled/world units?)\n\n    // User formula: newX = (mouseX - canvasRect.left) / zoom - dragOffset.x - panOffset.x / zoom\n\n    // So when we start drag:\n    // table.x = (mouseX - rect.left)/zoom - dragOffset.x - panOffset.x/zoom\n    // dragOffset.x = (mouseX - rect.left)/zoom - table.x - panOffset.x/zoom\n\n    const mouseX = e.clientX;\n    const mouseY = e.clientY;\n\n    setDraggedTable(table.name);\n    setDragOffset({\n      x: (mouseX - canvasRect.left) / zoom - table.x - panOffset.x / zoom,\n      y: (mouseY - canvasRect.top) / zoom - table.y - panOffset.y / zoom\n    });\n  };\n\n  const TableShape = ({ table }: { table: typeof tablesWithPosition[0] }) => {\n    const dimensions = getTableDimensions(table.table_shape, table.no_of_seats);\n\n    const baseClasses = cn(\n      'absolute border-2 flex items-center justify-center text-sm font-semibold cursor-pointer transition-all select-none',\n      getTableStatusColor(table.occupied),\n      isEditMode && 'hover:ring-2 hover:ring-blue-400 cursor-move',\n      draggedTable === table.name && 'shadow-xl scale-105 z-20',\n      selectedTable === table.name && 'ring-2 ring-blue-600 z-10'\n    );\n\n    const style = {\n      left: table.x,\n      top: table.y,\n      width: dimensions.width,\n      height: dimensions.height,\n      transform: `scale(${zoom})`,\n      transformOrigin: 'top left',\n    };\n\n    const shapeLower = table.table_shape?.toLowerCase();\n    const shapeClasses = {\n      circle: 'rounded-full',\n      square: 'rounded-lg',\n      rectangle: 'rounded-md'\n    };\n\n    const roundedClass = shapeClasses[shapeLower as keyof typeof shapeClasses] || shapeClasses.rectangle;\n\n    return (\n      <div\n        className={cn(baseClasses, roundedClass)}\n        style={style}\n        onMouseDown={(e) => handleMouseDown(e, table)}\n      >\n        <div className=\"text-center p-1 overflow-hidden pointer-events-none\">\n          <div className=\"font-bold truncate px-1\">{table.name}</div>\n          <div className=\"text-[10px] flex items-center justify-center gap-1 opacity-80\">\n            <Users className=\"w-3 h-3\" />\n            {table.no_of_seats || '-'}\n          </div>\n        </div>\n        {isEditMode && (\n          <>\n            <div className=\"absolute -top-1 -right-1 bg-blue-500 text-white rounded-full p-0.5 shadow-sm\">\n              <Move className=\"w-2 h-2\" />\n            </div>\n          </>\n        )}\n      </div>\n    );\n  };\n\n  // Helper to format invoice time (consistent with Table.tsx) removed - imported from utils\n  const handleCapacityChange = (capacityStr: string) => {\n    if (!selectedTable) return;\n\n    // Always update the input field value to allow free typing\n    setCapacityInput(capacityStr);\n\n    const capacity = parseInt(capacityStr);\n    if (isNaN(capacity) || capacity < 1 || capacity > 20) return;\n\n    const currentTable = tablesWithPosition.find(t => t.name === selectedTable);\n    if (!currentTable) return;\n\n    setLocalLayouts(prev => ({\n      ...prev,\n      [selectedTable]: {\n        ...(prev[selectedTable] || {}),\n        no_of_seats: capacity\n      }\n    }));\n\n    updateTableLayout(selectedTable, { no_of_seats: capacity })\n      .catch(console.error);\n  }\n\n  const handleDropdownShapeChange = (shape: string) => {\n    if (!selectedTable) return;\n    const currentTable = tablesWithPosition.find(t => t.name === selectedTable);\n    if (!currentTable) return;\n\n    setLocalLayouts(prev => ({\n      ...prev,\n      [selectedTable]: {\n        ...(prev[selectedTable] || {}),\n        table_shape: shape as any\n      }\n    }));\n\n    updateTableLayout(selectedTable, { table_shape: shape as any })\n      .catch(console.error);\n  }\n\n  const selectedTableData = tablesWithPosition.find(t => t.name === selectedTable);\n\n  return (\n    <div className=\"flex flex-col h-full bg-gray-50\">\n      {/* Header Controls */}\n      <div className=\"bg-white border-b border-gray-200 p-4\">\n        <div className=\"flex items-center justify-between\">\n          <div className=\"flex items-center gap-4\">\n            <Button\n              onClick={onBackToGrid}\n              variant=\"outline\"\n              className=\"flex items-center gap-2\"\n            >\n              <Grid3X3 className=\"w-4 h-4\" />\n              {t('tables.grid_view')}\n            </Button>\n            <h2 className=\"text-lg font-semibold\">{selectedRoom} <span className=\"text-gray-400 mx-2\">|</span> {t('tables.layout')}</h2>\n          </div>\n          <div className=\"flex items-center gap-2\">\n            {/* Edit Mode Toggle */}\n            <div className=\"\">\n              <button\n                onClick={() => {\n                  if (isEditMode) {\n                    onRefresh?.();\n                  }\n                  setIsEditMode(!isEditMode);\n                }}\n                className={cn(\n                  'flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium border transition-all',\n                  isEditMode\n                    ? 'bg-blue-600 hover:bg-blue-700 text-white border-green-700'\n                    : 'bg-white hover:bg-gray-50 text-gray-700 border-gray-200'\n                )}\n              >\n                {isEditMode ? <Save className=\"w-4 h-4\" /> : <Edit3 className=\"w-4 h-4\" />}\n                {isEditMode ? t('tables.finish_editing') : t('tables.edit_layout')}\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      {/* Canvas Area */}\n      <div className=\"flex-1 relative \">\n        {/* Zoom Controls */}\n        <div className=\"absolute top-4 left-4 z-30 flex flex-col gap-2\">\n          <button\n            onClick={handleZoomIn}\n            className=\"p-2 bg-white hover:bg-gray-50 rounded-lg shadow-lg border border-gray-200 transition-colors\"\n            title=\"Zoom In\"\n          >\n            <ZoomIn className=\"w-5 h-5 text-gray-700\" />\n          </button>\n          <button\n            onClick={handleZoomOut}\n            className=\"p-2 bg-white hover:bg-gray-50 rounded-lg shadow-lg border border-gray-200 transition-colors\"\n            title=\"Zoom Out\"\n          >\n            <ZoomOut className=\"w-5 h-5 text-gray-700\" />\n          </button>\n          <button\n            onClick={handleResetZoom}\n            className=\"p-2 bg-white hover:bg-gray-50 rounded-lg shadow-lg border border-gray-200 transition-colors\"\n            title=\"Reset Zoom & Pan\"\n          >\n            <RotateCcw className=\"w-5 h-5 text-gray-700\" />\n          </button>\n          <div className=\"px-2 py-1 bg-white rounded-lg shadow-lg border border-gray-200 text-xs font-medium text-gray-600\">\n            {Math.round(zoom * 100)}%\n          </div>\n        </div>\n\n        {/* Instructions */}\n        <div className=\"absolute bottom-4 right-4 z-30 pointer-events-none\">\n          {isEditMode ? (\n            <div className=\"bg-blue-50/90 backdrop-blur border border-blue-200 rounded-lg p-3 text-sm text-blue-800 shadow-lg\">\n              <div className=\"font-medium mb-1\">{t('tables.editing_layout_hint_title')}</div>\n              <div>{t('tables.drag_tables_hint')}</div>\n              <div>{t('tables.autosave_hint')}</div>\n            </div>\n          ) : (\n            <div className=\"bg-white/80 backdrop-blur border border-gray-200 rounded-lg p-2 text-xs text-gray-500 shadow-sm\">\n              {t('tables.zoom_pan_hint')}\n            </div>\n          )}\n        </div>\n\n        <div\n          ref={canvasRef}\n          className=\"w-full h-full relative bg-white overflow-hidden cursor-grab active:cursor-grabbing\"\n          style={{\n            backgroundImage: `\n              linear-gradient(to right, #e5e7eb 1px, transparent 1px),\n              linear-gradient(to bottom, #e5e7eb 1px, transparent 1px)\n            `,\n            backgroundSize: `${20 * zoom}px ${20 * zoom}px`,\n            backgroundPosition: `${panOffset.x}px ${panOffset.y}px`\n          }}\n          onMouseDown={handleCanvasMouseDown}\n          onMouseMove={handleCanvasMouseMove}\n          onMouseUp={handleCanvasMouseUp}\n          onMouseLeave={handleCanvasMouseUp}\n        >\n          {/* Tables Container with Transform */}\n          <div\n            style={{\n              transform: `translate(${panOffset.x}px, ${panOffset.y}px)`,\n              transformOrigin: 'top left'\n            }}\n          >\n            {tablesWithPosition.map(table => (\n              <TableShape key={table.name} table={table} />\n            ))}\n          </div>\n        </div>\n      </div>\n\n      {/* Table Properties Panel */}\n      {selectedTable && selectedTableData && (\n        <div className={cn(\"absolute bottom-0 top-36 bg-white rounded-t-lg shadow-xl border-t border-l border-gray-200 p-4 w-full max-w-xs z-40 max-h-[72vh] overflow-y-auto\", isRTL ? \"left-0 border-r\" : \"right-0\")}>\n          <div className=\"flex justify-between items-center mb-3\">\n            <h4 className=\"font-semibold text-gray-900\">\n              {isEditMode ? t('tables.edit_settings') : t('tables.table_info')}\n            </h4>\n            <button\n              onClick={() => setSelectedTable(null)}\n              className=\"p-1 hover:bg-gray-100 rounded\"\n            >\n              <X className=\"w-4 h-4\" />\n            </button>\n          </div>\n\n          <div className=\"space-y-3\">\n            <div>\n              <label className=\"block text-sm font-medium mb-1\">{t('tables.table_name')}</label>\n              <input\n                type=\"text\"\n                value={selectedTableData.name}\n                disabled={true}\n                className=\"w-full px-3 py-2 border rounded-md text-sm border-gray-200 bg-gray-50 cursor-not-allowed\"\n                title={t('tables.table_name_title')}\n              />\n            </div>\n\n            <div>\n              <label className=\"block text-sm font-medium mb-1\">{t('tables.capacity')}</label>\n              <input\n                type=\"number\"\n                min=\"0\"\n                max=\"20\"\n                value={capacityInput}\n                onChange={(e) => handleCapacityChange(e.target.value)}\n                disabled={!isEditMode}\n                className={cn(\n                  \"w-full px-3 py-2 border rounded-md text-sm\",\n                  isEditMode\n                    ? \"border-gray-300 bg-white\"\n                    : \"border-gray-200 bg-gray-50 cursor-not-allowed\"\n                )}\n                placeholder={t('tables.capacity_placeholder')}\n              />\n              <p className=\"text-xs text-gray-500 mt-1\">{t('tables.capacity_range_hint')}</p>\n            </div>\n\n            <div>\n              <label className=\"block text-sm font-medium mb-1\">{t('tables.shape')}</label>\n              <select\n                value={selectedTableData.table_shape || 'Rectangle'}\n                onChange={(e) => handleDropdownShapeChange(e.target.value)}\n                disabled={!isEditMode}\n                className={cn(\n                  \"w-full px-3 py-2 border rounded-md text-sm\",\n                  isEditMode\n                    ? \"border-gray-300 bg-white\"\n                    : \"border-gray-200 bg-gray-50 cursor-not-allowed\"\n                )}\n              >\n                <option value=\"Circle\">{t('tables.circle')}</option>\n                <option value=\"Square\">{t('tables.square')}</option>\n                <option value=\"Rectangle\">{t('tables.rectangle')}</option>\n              </select>\n            </div>\n\n            <div>\n              <label className=\"block text-sm font-medium mb-1\">{t('tables.status')}</label>\n              <div className=\"w-full px-3 py-2 border border-gray-200 bg-gray-50 rounded-md text-sm cursor-not-allowed capitalize\">\n                {selectedTableData.occupied ? t('tables.occupied') : t('tables.available')}\n              </div>\n            </div>\n\n            {/* Position Information */}\n            <div className=\"pt-3 border-t border-gray-200\">\n              <label className=\"block text-sm font-medium mb-2\">{t('tables.position')}</label>\n              <div className={cn(\"grid grid-cols-2 gap-2 text-sm\", isRTL && \"flex-row-reverse\")}>\n                <div>\n                  <span className=\"text-gray-500\">X:</span>\n                  <span className=\"ml-1\">{Math.round(selectedTableData.x)}px</span>\n                </div>\n                <div>\n                  <span className=\"text-gray-500\">Y:</span>\n                  <span className=\"ml-1\">{Math.round(selectedTableData.y)}px</span>\n                </div>\n              </div>\n            </div>\n\n            {/* Size Information */}\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">{t('tables.size')}</label>\n              <div className={cn(\"grid grid-cols-2 gap-2 text-sm\", isRTL && \"flex-row-reverse\")}>\n                <div>\n                  <span className=\"text-gray-500\">W:</span>\n                  <span className=\"ml-1\">{getTableDimensions(selectedTableData.table_shape || 'Rectangle').width}px</span>\n                </div>\n                <div>\n                  <span className=\"text-gray-500\">H:</span>\n                  <span className=\"ml-1\">{getTableDimensions(selectedTableData.table_shape || 'Rectangle').height}px</span>\n                </div>\n              </div>\n            </div>\n\n            {/* Show current bill info if table is occupied */}\n            {selectedTableData.latest_invoice_time && (\n              <div className=\"pt-3 border-t border-gray-200\">\n                <label className=\"block text-sm font-medium mb-2\">{t('tables.current_bill')}</label>\n                <div className=\"bg-blue-50 p-3 rounded-md text-sm\">\n                  <div className=\"flex justify-between mb-1\">\n                    <span>{t('tables.started_at')}</span>\n                    <span>{formatInvoiceTime(selectedTableData.latest_invoice_time)}</span>\n                  </div>\n                  {selectedTableOrder && (\n                    <div className=\"flex justify-between items-center pt-2 mt-2 border-t border-blue-200\">\n                      <span>{t('tables.total_amount')}</span>\n                      <span className=\"font-bold text-lg text-blue-800\">\n                        {selectedTableOrder.grand_total.toFixed(2)}\n                      </span>\n                    </div>\n                  )}\n                </div>\n              </div>\n            )}\n\n          </div>\n        </div>\n      )}\n    </div>\n  );\n};\n\nexport default LayoutView;"
  },
  {
    "path": "pos/src/components/MenuCard.tsx",
    "content": "import { FC } from 'react';\nimport { formatCurrency, cn } from '../lib/utils';\n\ninterface MenuCardProps {\n  id: string;\n  name: string;\n  price: number;\n  item_image: string | null;\n  course?: string;\n  item: string;\n  onClick?: () => void;\n  disabled?: boolean;\n}\n\nconst MenuCard: FC<MenuCardProps> = ({ \n  id, \n  name, \n  price, \n  item_image, \n  course, \n  item, \n  onClick,\n  disabled \n}) => {\n  return (\n    <div\n      className={cn(\n        \"bg-white rounded-lg shadow-sm overflow-hidden hover:shadow-md transition-shadow cursor-pointer h-56 flex flex-col\",\n        disabled && \"opacity-50 cursor-not-allowed pointer-events-none\"\n      )}\n      onClick={disabled ? undefined : onClick}\n    >\n      {/* Image section - fixed height */}\n      <div className=\"h-24\">\n        {item_image ? (\n          <img\n            src={item_image}\n            alt={name}\n            className=\"w-full h-full object-cover filter saturate-75 brightness-95\"\n            style={{ filter: 'saturate(0.7) brightness(0.95)' }}\n            onError={(e) => {\n              const target = e.target as HTMLImageElement;\n              target.style.display = 'none';\n              const parent = target.parentElement;\n              if (parent) {\n                const placeholder = document.createElement('div');\n                placeholder.className = 'w-full h-full bg-gray-200 flex items-center justify-center text-2xl text-gray-400 font-medium';\n                placeholder.textContent = name.slice(0, 2).toUpperCase();\n                parent.insertBefore(placeholder, target);\n              }\n            }}\n          />\n        ) : (\n          <div className=\"w-full h-full bg-gray-200 flex items-center justify-center text-2xl text-gray-400 font-medium\">\n            {name.slice(0, 2).toUpperCase()}\n          </div>\n        )}\n      </div>\n\n      {/* Content section - flex grow with fixed padding */}\n      <div className=\"flex-1 p-3 flex flex-col\">\n        {/* Name section - fixed height for 2 lines */}\n        <div className=\"\">\n          <h3 className=\"font-medium text-gray-900 text-sm leading-5 line-clamp-2\" title={name}>\n            {name}\n          </h3>\n        </div>\n\n        {/* Course section - fixed height for 1 line */}\n        <div className=\"h-5 mt-1\">\n          <p className=\"text-xs text-gray-500 truncate\" title={course}>\n            {course || ' '}\n          </p>\n        </div>\n\n        {/* Price section - pushed to bottom */}\n        <div className=\"mt-auto pt-2\">\n          <span className=\"text-sm font-semibold text-gray-900 tabular-nums\">\n            {formatCurrency(price)}\n          </span>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default MenuCard; "
  },
  {
    "path": "pos/src/components/MenuList.tsx",
    "content": "import { useEffect, useMemo } from 'react';\nimport { usePOSStore } from '../store/pos-store';\nimport MenuCard from './MenuCard';\nimport { Spinner } from './ui/spinner';\nimport { cn } from '../lib/utils';\nimport { t } from '../i18n';\n\ninterface MenuListProps {\n  onItemClick: (item: any) => void;\n}\n\nconst MenuList: React.FC<MenuListProps> = ({ onItemClick }) => {\n  const {\n    menuItems,\n    menuLoading,\n    error,\n    selectedCategory,\n    searchQuery,\n    quickFilter,\n    fetchMenuItems,\n    isMenuInteractionDisabled,\n    isOrderInteractionDisabled\n  } = usePOSStore();\n\n  useEffect(() => {\n    fetchMenuItems();\n  }, [fetchMenuItems]);\n\n  const filteredItems = useMemo(() => {\n    return menuItems.filter(item => {\n      const searchTerm = searchQuery.toLowerCase();\n      const matchesCategory = !selectedCategory || item.course === selectedCategory;\n      const matchesSearch = !searchQuery || \n        item.name.toLowerCase().includes(searchTerm) ||\n        item.item.toLowerCase().includes(searchTerm);\n      const matchesFilter = quickFilter === 'all' || \n        (quickFilter === 'special' && item.special_dish === 1);\n      \n      return matchesCategory && matchesSearch && matchesFilter;\n    });\n  }, [menuItems, selectedCategory, searchQuery, quickFilter]);\n\n  const isInteractionDisabled = isMenuInteractionDisabled() || isOrderInteractionDisabled();\n\n  return (\n    <div className=\"flex-1 overflow-auto bg-gray-50\">\n      <div className=\"max-w-screen-xl mx-auto p-4 pb-40\">\n        {menuLoading ? (\n          <div className=\"h-96\">\n            <Spinner message={t('common.loading_menu_items')} />\n          </div>\n        ) : error ? (\n          <div className=\"flex items-center justify-center h-96\">\n            <div className=\"text-red-600 text-center\">\n              <p className=\"text-lg font-medium\">{t('common.error_loading_menu_items')}</p>\n              <p className=\"text-sm mt-2\">{error}</p>\n            </div>\n          </div>\n        ) : filteredItems.length === 0 ? (\n          <div className=\"flex items-center justify-center h-96\">\n            <div className=\"text-gray-500 text-center\">\n              <p className=\"text-lg font-medium\">{t('common.no_items_found')}</p>\n              <p className=\"text-sm mt-2\">{t('common.try_adjusting_filters')}</p>\n            </div>\n          </div>\n        ) : (\n          <div className={cn(\n            \"grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3\",\n            isInteractionDisabled && \"opacity-50 pointer-events-none\"\n          )}>\n            {filteredItems.map((item) => (\n              <MenuCard\n                key={item.id}\n                id={item.id}\n                name={item.name}\n                price={item.price}\n                item_image={item.image}\n                course={item.course_label || item.course}\n                item={item.item}\n                onClick={() => onItemClick(item)}\n                disabled={isInteractionDisabled}\n              />\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n};\n\nexport default MenuList; "
  },
  {
    "path": "pos/src/components/OrderPanel.tsx",
    "content": "import { useState } from 'react';\nimport { Trash2, Edit, FrownIcon, Plus, Loader2, MessageSquare } from 'lucide-react';\nimport { usePOSStore } from '../store/pos-store';\nimport { formatCurrency, cn } from '../lib/utils';\nimport { CustomerSelect } from './CustomerSelect';\nimport ProductDialog from './ProductDialog';\nimport OrderTypeSelect from './OrderTypeSelect';\nimport CommentDialog from './CommentDialog';\nimport { Button } from './ui/button';\nimport { Spinner } from './ui/spinner';\nimport { syncOrder } from '../lib/order-api';\nimport { useRootStore } from '../store/root-store';\nimport type { RootState } from '../store/root-store';\nimport { showToast } from './ui/toast';\nimport { DINE_IN } from '../data/order-types';\nimport { t } from '../i18n';\n\nconst OrderPanel = () => {\n  const { \n    activeOrders, \n    removeFromOrder, \n    updateQuantity, \n    clearOrder, \n    setSelectedItem,\n    orderLoading,\n    isOrderInteractionDisabled,\n    isUpdatingOrder,\n    posProfile,\n    selectedOrderType,\n    selectedTable,\n    selectedRoom,\n    selectedCustomer,\n    selectedAggregator,\n    resetOrderState,\n    paymentModes,\n    orderId,\n    orderComment,\n    setOrderComment\n  } = usePOSStore();\n  const user = useRootStore((state: RootState) => state.user);\n  const [editingItem, setEditingItem] = useState<typeof activeOrders[0] | null>(null);\n  const [isSubmitting, setIsSubmitting] = useState(false);\n  const [showCommentDialog, setShowCommentDialog] = useState(false);\n\n  const calculateItemTotal = (item: typeof activeOrders[0]) => {\n    const basePrice = item.selectedVariant?.price || item.price;\n    const addonsTotal = item.selectedAddons?.reduce((sum, addon) => sum + addon.price, 0) || 0;\n    return (basePrice + addonsTotal) * item.quantity;\n  };\n\n  const total = activeOrders.reduce(\n    (sum, item) => sum + calculateItemTotal(item),\n    0\n  );\n\n  const handleEdit = (item: typeof activeOrders[0]) => {\n    const menuItem = {\n      ...item,\n      variants: item.variants,\n      addons: item.addons,\n    };\n    setSelectedItem(menuItem);\n    setEditingItem(item);\n  };\n\n  const handleCommentSave = (comment: string) => {\n    setOrderComment(comment);\n  };\n\n  const handleSubmit = async () => {\n    try {\n      if (!posProfile) {\n        throw new Error(t('errors.pos_profile_not_found'));\n      }\n\n      if (!user?.name) {\n        throw new Error(t('errors.user_not_logged_in'));\n      }\n\n      // Validate customer/aggregator details\n      if (selectedOrderType === 'Aggregators') {\n        if (!selectedAggregator?.customer) {\n          showToast.error(t('errors.select_aggregator'));\n          return;\n        }\n      } else if (!selectedCustomer?.name) {\n        showToast.error(t('errors.select_customer'));\n        return;\n      }\n\n      // Validate table selection for dine-in orders\n      if (selectedOrderType === DINE_IN && !selectedTable) {\n        showToast.error(t('errors.select_table', { order_type: DINE_IN }));\n        return;\n      }\n\n      setIsSubmitting(true);\n      \n      const orderData = {\n        items: activeOrders.map(item => ({\n          item: item.id,\n          item_name: item.name,\n          rate: item.selectedVariant?.price || item.price,\n          qty: item.quantity,\n          comment: item.comment || undefined\n        })),\n        no_of_pax: 1,\n        pos_profile: posProfile.name,\n        order_type: selectedOrderType,\n        table: selectedTable || undefined,\n        room: selectedRoom || undefined,\n        customer: selectedOrderType === 'Aggregators' ? selectedAggregator?.customer : selectedCustomer?.name,\n        aggregator_id: selectedOrderType === 'Aggregators' ? selectedAggregator?.customer : undefined,\n        cashier: posProfile.cashier,\n        owner: user.name,\n        mode_of_payment: paymentModes[0],\n        last_invoice: isUpdatingOrder ? orderId : null,\n        invoice: isUpdatingOrder ? orderId : null,\n        waiter: user.name,\n        comments: orderComment || undefined\n      };\n\n      await syncOrder(orderData);\n      \n      // Reset all states after successful order submission\n      resetOrderState();\n      showToast.success(isUpdatingOrder ? t('success.order_updated') : t('success.order_created'));\n    } catch (error) {\n      console.error('Failed to sync order:', error);\n      // Frappe API error handling\n      if (error && typeof error === 'object' && '_server_messages' in error && typeof (error as any)._server_messages === 'string') {\n        try {\n          const messages = JSON.parse((error as any)._server_messages);\n          const messageObj = JSON.parse(messages[0]);\n          showToast.error(messageObj.message || 'API error');\n        } catch {\n          showToast.error('API error');\n        }\n      } else if (error instanceof Error) {\n        showToast.error(error.message);\n      } else {\n        showToast.error(t('errors.failed_process_order'));\n      }\n    } finally {\n      setIsSubmitting(false);\n    }\n  };\n\n  const EmptyCartUI = () => (\n    <div className=\"flex-1 flex flex-col items-center justify-center p-8 text-center\">\n      <div className=\"w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mb-6\">\n        <FrownIcon className=\"w-12 h-12 text-gray-400\" />\n      </div>\n      \n      <h3 className=\"text-lg font-semibold text-gray-900 mb-2\">\n        {t('cart.empty_title')}\n      </h3>\n\n      <p className=\"text-gray-500 text-sm mb-6 max-w-xs leading-relaxed\">\n        {t('cart.empty_subtitle')}\n      </p>\n\n      <div className=\"flex items-center gap-2 text-blue-600 bg-blue-50 px-4 py-2 rounded-lg\">\n        <Plus className=\"w-4 h-4\" />\n        <span className=\"text-sm font-medium\">{t('cart.click_to_add')}</span>\n      </div>\n\n      <div className=\"mt-4 text-xs text-gray-400\">\n        {t('cart.double_click_hint')}\n      </div>\n    </div>\n  );\n\n  const LoadingOrderUI = () => (\n    <div className=\"h-96\">\n      <Spinner message={t('cart.loading_order')} />\n    </div>\n  );\n\n  const isInteractionDisabled = isOrderInteractionDisabled() || isSubmitting;\n\n  return (\n    <div className=\"w-96 bg-white border-s border-gray-200 flex flex-col h-[calc(100vh-4rem)] fixed end-0 z-10\">\n      <div className=\"p-4 border-b border-gray-200 flex-shrink-0\">\n        <OrderTypeSelect disabled={isInteractionDisabled} />\n        <div className=\"mt-3\"><CustomerSelect disabled={isInteractionDisabled} /></div>\n      </div>\n      \n      {orderLoading ? (\n        <LoadingOrderUI />\n      ) : activeOrders.length === 0 ? (\n        <EmptyCartUI />\n      ) : (\n        <>\n          <div className=\"flex-1 overflow-y-auto px-6\">\n            {activeOrders.map((item) => (\n              <div\n                key={item.uniqueId}\n                className={cn(\n                  \"flex flex-col py-4 border-b border-gray-100\",\n                  isInteractionDisabled && \"opacity-50\"\n                )}\n              >\n                <div className=\"flex items-center justify-between\">\n                  <div className=\"flex-1\">\n                    <div className=\"flex items-center justify-between\">\n                      <h3 className=\"font-medium text-gray-900 text-sm\">{item.name}</h3>\n                    </div>\n                    {item.selectedVariant && (\n                      <p className=\"text-sm text-gray-600\">{item.selectedVariant.name}</p>\n                    )}\n                    {item.selectedAddons && item.selectedAddons.length > 0 && (\n                      <p className=\"text-sm text-gray-500\">\n                        {item.selectedAddons.map(addon => addon.name).join(', ')}\n                      </p>\n                    )}\n                    <p className=\"text-gray-600 text-sm\">{formatCurrency(calculateItemTotal(item))}</p>\n                  </div>\n                  \n                  <div className=\"flex items-center gap-2\">\n                    <Button\n                      onClick={() => handleEdit(item)}\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"text-blue-600 hover:text-blue-700\"\n                      title={t('cart.edit_item')}\n                      disabled={isInteractionDisabled}\n                    >\n                      <Edit className=\"w-4 h-4\" />\n                    </Button>\n                    <div className=\"flex items-center gap-2\">\n                      <Button\n                        onClick={() => {\n                          const newQuantity = Math.max(0, item.quantity - 1);\n                          if (newQuantity === 0) {\n                            removeFromOrder(item.uniqueId!);\n                          } else {\n                            updateQuantity(item.uniqueId!, newQuantity);\n                          }\n                        }}\n                        variant=\"outline\"\n                        size=\"icon\"\n                        className=\"w-8 h-8 rounded-full\"\n                        disabled={isInteractionDisabled}\n                      >\n                        -\n                      </Button>\n                      <span className=\"w-6 text-center\">{item.quantity}</span>\n                      <Button\n                        onClick={() => updateQuantity(item.uniqueId!, item.quantity + 1)}\n                        variant=\"outline\"\n                        size=\"icon\"\n                        className=\"w-8 h-8 rounded-full\"\n                        disabled={isInteractionDisabled}\n                      >\n                        +\n                      </Button>\n                    </div>\n                    \n                    <Button\n                      onClick={() => removeFromOrder(item.uniqueId!)}\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"text-red-500 hover:text-red-600\"\n                      disabled={isInteractionDisabled}\n                    >\n                      <Trash2 className=\"w-5 h-5\" />\n                    </Button>\n                  </div>\n                </div>\n              </div>\n            ))}\n            {activeOrders.length > 0 && (\n              <Button\n                onClick={clearOrder}\n                variant=\"ghost\"\n                size=\"sm\"\n                className=\"w-full text-gray-600 hover:text-gray-800 mt-4\"\n                disabled={isInteractionDisabled}\n              >\n                {t('cart.clear_cart')}\n              </Button>\n            )}\n          </div>\n          \n          <div className=\"p-4 border-t border-gray-200 flex-shrink-0 bg-white\">\n            <div className=\"flex justify-between items-center mb-4\">\n              <div className=\"flex items-center gap-2\">\n                <Button\n                  onClick={() => setShowCommentDialog(true)}\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  className={cn(\n                    \"h-8 w-8 p-0\",\n                    orderComment ? \"text-blue-600\" : \"text-gray-500 hover:text-gray-700\"\n                  )}\n                  disabled={isInteractionDisabled}\n                  title={orderComment ? t('cart.edit_comment') : t('cart.add_comment')}\n                >\n                  <MessageSquare className=\"w-4 h-4\" />\n                </Button>\n                <span className=\"text-lg font-semibold\">{t('cart.total')}</span>\n              </div>\n              <span className=\"text-lg font-semibold\">{formatCurrency(total)}</span>\n            </div>\n            <Button\n              onClick={handleSubmit}\n              variant=\"default\"\n              size=\"default\"\n              className=\"w-full\"\n              disabled={isInteractionDisabled}\n            >\n              {isSubmitting ? (\n                <div className=\"flex items-center\">\n                  <Loader2 className=\"w-4 h-4 me-2 animate-spin\" />\n\n                  {isUpdatingOrder ? t('cart.updating_order') : t('cart.processing_order')}\n                </div>\n              ) : isUpdatingOrder ? (\n                t('cart.update_order')\n              ) : (\n                t('cart.add_new_order')\n              )}\n            </Button>\n          </div>\n        </>\n      )}\n\n      {editingItem && (\n        <ProductDialog\n          onClose={() => {\n            setEditingItem(null);\n            setSelectedItem(null);\n          }}\n          editMode\n          initialVariant={editingItem.selectedVariant}\n          initialAddons={editingItem.selectedAddons}\n          initialQuantity={editingItem.quantity}\n          itemToReplace={editingItem}\n        />\n      )}\n\n      <CommentDialog\n        isOpen={showCommentDialog}\n        onClose={() => setShowCommentDialog(false)}\n        onSave={handleCommentSave}\n        initialComment={orderComment}\n      />\n    </div>\n  );\n};\n\nexport default OrderPanel; "
  },
  {
    "path": "pos/src/components/OrderStatusSidebar.tsx",
    "content": "import { FileText } from 'lucide-react';\nimport { cn } from '../lib/utils';\nimport { Button } from './ui';\nimport { getOrderStatusTypes, OrderStatusType } from '../data/order-types';\nimport { usePOSStore } from '../store/pos-store';\nimport { t } from '../i18n';\n\ninterface OrderStatusSidebarProps {\n  disabled?: boolean;\n  selectedStatus: OrderStatusType;\n  setSelectedStatus: (status: OrderStatusType) => void;\n  getStatusCount?: (status: OrderStatusType) => number;\n}\n\nconst OrderStatusSidebar = ({ \n  disabled,\n  selectedStatus,\n  setSelectedStatus,\n}: OrderStatusSidebarProps) => {\n  const { posProfile } = usePOSStore();\n  \n  // Get the appropriate status types based on POS profile settings\n  const statusTypes = getOrderStatusTypes(posProfile?.view_all_status, posProfile?.paid_limit);\n\n  return (\n    <div className={cn(\n      \"w-64 bg-white border-e border-gray-200 h-full flex flex-col\",\n      disabled && \"opacity-50 pointer-events-none\"\n    )}>\n      <nav className=\"flex-1 p-6 overflow-y-auto\">\n        <div className=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n          {/* Section Title */}\n          <h2 className=\"text-xs font-medium text-gray-500 uppercase tracking-wide mb-3 px-1\">\n            {t('orders.status_title')}\n          </h2>\n\n          {/* Status Items */}\n          <div className=\"space-y-1\">\n            {statusTypes.map((status) => (\n              <Button\n                key={status.value}\n                onClick={() => setSelectedStatus(status.value as OrderStatusType)}\n                variant=\"ghost\"\n                className={cn(\n                  'w-full flex items-center justify-between px-3 py-2.5 text-sm font-medium transition-all duration-200 group relative',\n                  selectedStatus === status.value\n                    ? 'bg-white text-gray-900 shadow-sm font-semibold'\n                    : 'text-gray-700 hover:bg-white/60 hover:text-gray-900'\n                )}\n                disabled={disabled}\n              >\n                {/* Active indicator bar */}\n                {selectedStatus === status.value && (\n                  <div className=\"absolute start-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-blue-600 rounded-e-full\" />\n                )}\n                <div className=\"flex items-center gap-3 ms-1\">\n                  <FileText className=\"w-4 h-4 text-gray-500\" />\n                  <span>{t(`order_status_types.${status.value.toLowerCase().replace(/ /g, '_')}`)}</span>\n                </div>\n              </Button>\n            ))}\n          </div>\n        </div>\n      </nav>\n    </div>\n  );\n};\n\nexport default OrderStatusSidebar; "
  },
  {
    "path": "pos/src/components/OrderTypeSelect.tsx",
    "content": "import { useState } from 'react';\nimport { usePOSStore } from '../store/pos-store';\nimport { useRootStore } from '../store/root-store';\nimport { cn } from '../lib/utils';\nimport { Button } from './ui';\nimport TableSelectionDialog from './TableSelectionDialog';\nimport { DEFAULT_ORDER_TYPE, DINE_IN, ORDER_TYPES , type OrderType} from '../data/order-types';\nimport { HandPlatter } from 'lucide-react';\nimport { isUserRestrictedFromTableOrders } from '../lib/role-utils';\nimport { t } from '../i18n';\n\ninterface OrderTypeSelectProps {\n  disabled?: boolean;\n}\n\nconst OrderTypeSelect = ({ disabled }: OrderTypeSelectProps) => {\n  const { selectedOrderType, setSelectedOrderType, selectedTable, posProfile, isUpdatingOrder } = usePOSStore();\n  const { user } = useRootStore();\n  const [showTableDialog, setShowTableDialog] = useState(false);\n\n  // Check if user is restricted from table orders\n  const isRestrictedFromTableOrders = isUserRestrictedFromTableOrders(user, posProfile);\n\n  const handleOrderTypeSelect = (type: OrderType) => {\n    // Prevent selecting \"Dine In\" if user is restricted\n    if (type === DINE_IN && isRestrictedFromTableOrders) {\n      return;\n    }\n    \n    setSelectedOrderType(type);\n    if (type === DINE_IN) {\n      setShowTableDialog(true);\n    }\n  };\n\n  const handleTableDialogClose = () => {\n    setShowTableDialog(false);\n    // Use a timeout to allow state to update before checking\n    setTimeout(() => {\n      const currentState = usePOSStore.getState();\n      if (currentState.selectedOrderType === DINE_IN && !currentState.selectedTable) {\n        setSelectedOrderType(DEFAULT_ORDER_TYPE);\n      }\n    }, 100);\n  };\n\n  return (\n    <div>\n      <div className=\"flex gap-2 overflow-x-auto pb-2 -mx-2 px-2\">\n        {ORDER_TYPES.map(({ value, icon: Icon }) => {\n          const isDineIn = value === DINE_IN;\n          const isDisabled = disabled || (isDineIn && isRestrictedFromTableOrders) || isUpdatingOrder;\n          \n          return (\n            <Button\n              key={value}\n              onClick={() => handleOrderTypeSelect(value)}\n              variant={selectedOrderType === value ? 'default' : 'outline'}\n              className={cn(\n                'h-fit flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium whitespace-nowrap bg-white border transition-colors',\n                selectedOrderType === value\n                ? 'text-primary-700 bg-primary-50 border-primary-600 hover:bg-primary-50'\n                : 'text-gray-700 border-gray-200 hover:bg-gray-50',\n                isDisabled && 'opacity-50 cursor-not-allowed'\n              )}\n              disabled={isDisabled}\n              title={isDineIn && isRestrictedFromTableOrders ? t('errors.dine_in_restricted') || 'Dine In is not available for your role' : undefined}\n            >\n              <Icon className=\"w-4 h-4\" />\n              {t(`order_types.${value.toLowerCase().replace(/ /g, '_')}`)}\n            </Button>\n          );\n        })}\n      </div>\n\n      {selectedOrderType === DINE_IN && selectedTable && (\n        <Button\n          onClick={() => setShowTableDialog(true)}\n          variant=\"ghost\"\n          className=\"h-fit w-fit gap-x-2 mt-2 text-sm text-primary-600 hover:text-primary-700\"\n          disabled={disabled}\n        >\n          <HandPlatter className=\"w-4 h-4\" /> {selectedTable}\n        </Button>\n      )}\n\n      {showTableDialog && (\n        <TableSelectionDialog onClose={handleTableDialogClose} />\n      )}\n    </div>\n  );\n};\n\nexport default OrderTypeSelect; "
  },
  {
    "path": "pos/src/components/POSOpeningDialog.tsx",
    "content": "import { RefreshCw, AlertTriangle, Monitor } from 'lucide-react';\nimport { Button } from './ui';\nimport { t } from '../i18n';\n\ninterface POSOpeningDialogProps {\n  onReload: () => void;\n  type: 'opening' | 'closing';\n}\n\nconst POSOpeningDialog = ({ onReload, type }: POSOpeningDialogProps) => {\n  const isOpeningIssue = type === 'opening';\n  \n  const handleSwitchToDesk = () => {\n    // Get the current domain and open /app in a new tab\n    const currentDomain = window.location.origin;\n    window.open(`${currentDomain}/app`, '_blank');\n  };\n  \n  return (\n    <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\n      <div className=\"bg-white rounded-lg p-8 max-w-md w-full mx-4 shadow-xl\">\n        <div className=\"text-center\">\n          {/* Icon */}\n          <div className={`mx-auto flex items-center justify-center h-16 w-16 rounded-full mb-6 ${\n            isOpeningIssue ? 'bg-red-100' : 'bg-orange-100'\n          }`}>\n            {isOpeningIssue ? (\n              <RefreshCw className=\"h-8 w-8 text-red-600\" />\n            ) : (\n              <AlertTriangle className=\"h-8 w-8 text-orange-600\" />\n            )}\n          </div>\n          \n          {/* Title */}\n          <h2 className=\"text-2xl font-bold text-gray-900 mb-4\">\n            {isOpeningIssue ? t('pos.not_opened_title') : t('pos.not_closed_title')}\n          </h2>\n\n          {/* Message */}\n          <p className=\"text-gray-600 mb-8 text-lg\">\n            {isOpeningIssue ? t('pos.not_opened_message') : t('pos.not_closed_message')}\n          </p>\n\n          {/* Buttons */}\n          <div className=\"space-y-3\">\n            <Button\n              onClick={onReload}\n              className=\"w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-colors duration-200\"\n            >\n              <RefreshCw className=\"w-5 h-5 mr-2\" />\n              {t('pos.reload_page')}\n            </Button>\n\n            <Button\n              onClick={handleSwitchToDesk}\n              variant=\"outline\"\n              className=\"w-full border-gray-300 text-gray-700 hover:bg-gray-50 font-medium py-3 px-6 rounded-lg transition-colors duration-200\"\n            >\n              <Monitor className=\"w-5 h-5 mr-2\" />\n              {t('pos.switch_to_desk')}\n            </Button>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default POSOpeningDialog; "
  },
  {
    "path": "pos/src/components/POSOpeningProvider.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { checkPOSOpening, validatePOSClose } from '../lib/pos-opening-api';\nimport { usePOSStore } from '../store/pos-store';\nimport POSOpeningDialog from './POSOpeningDialog';\nimport { t } from '../i18n';\n\ninterface POSOpeningProviderProps {\n  children: React.ReactNode;\n}\n\ntype ValidationType = 'opening' | 'closing' | null;\n\nconst POSOpeningProvider = ({ children }: POSOpeningProviderProps) => {\n  const [validationType, setValidationType] = useState<ValidationType>(null);\n  const [isLoading, setIsLoading] = useState(true);\n  const { posProfile } = usePOSStore();\n\n  const checkPOSStatus = async () => {\n    try {\n      setIsLoading(true);\n      \n      // First check if POS is opened\n      const openingResponse = await checkPOSOpening();\n      if (openingResponse.message === 1) {\n        // POS is not opened\n        setValidationType('opening');\n        return;\n      }\n\n      // If POS is opened, check if custom_daily_pos_close is enabled\n      if (posProfile?.custom_daily_pos_close === 1) {\n        try {\n          const closeResponse = await validatePOSClose(posProfile.name);\n          if (closeResponse.message === 'Failed') {\n            // Previous POS is not closed\n            setValidationType('closing');\n            return;\n          }\n        } catch (error) {\n          console.error('Failed to validate POS close status:', error);\n          // On error, assume POS is not closed for safety\n          setValidationType('closing');\n          return;\n        }\n      }\n\n      // All validations passed\n      setValidationType(null);\n    } catch (error) {\n      console.error('Failed to check POS opening status:', error);\n      // On error, assume POS is not opened for safety\n      setValidationType('opening');\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleReload = () => {\n    window.location.reload();\n  };\n\n  useEffect(() => {\n    // Only check if we have the POS profile loaded\n    if (posProfile) {\n      checkPOSStatus();\n    }\n  }, [posProfile]);\n\n  // Show loading state while checking\n  if (isLoading) {\n    return (\n      <div className=\"fixed inset-0 bg-white flex items-center justify-center z-50\">\n        <div className=\"text-center\">\n          <div className=\"animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4\"></div>\n          <p className=\"text-gray-600\">{t('common.checking_pos_status')}</p>\n        </div>\n      </div>\n    );\n  }\n\n  // Show dialog if there's a validation issue\n  if (validationType) {\n    return <POSOpeningDialog onReload={handleReload} type={validationType} />;\n  }\n\n  // Render children if all validations passed\n  return <>{children}</>;\n};\n\nexport default POSOpeningProvider; "
  },
  {
    "path": "pos/src/components/PaymentDialog.tsx",
    "content": "import React, { useState, useEffect } from 'react';\nimport { X, Percent, Coins } from 'lucide-react';\nimport { usePOSStore } from '../store/pos-store';\nimport { cn, formatCurrency } from '../lib/utils';\nimport { Button, Input, Dialog, DialogContent } from './ui';\nimport { call } from '../lib/frappe-sdk';\nimport { DEFAULT_PAYMENT_MODE } from '../data/order-types';\nimport { t } from '../i18n';\n\n\ninterface PaymentDialogProps {\n  onClose: () => void;\n  grandTotal: number;\n  roundedTotal: number;\n  invoice: string;\n  customer: string;\n  posProfile: string;\n  table: string | null;\n  cashier: string;\n  owner: string;\n  fetchOrders: () => Promise<void>;\n  clearSelectedOrder: () => void;\n}\n\nconst PaymentDialog: React.FC<PaymentDialogProps> = ({\n  onClose,\n  grandTotal,\n  roundedTotal,\n  invoice,\n  customer,\n  posProfile,\n  table,\n  cashier,\n  owner,\n  fetchOrders,\n  clearSelectedOrder\n}) => {\n  const { paymentModes, fetchPaymentModes, posProfile: storePosProfile } = usePOSStore();\n  const [isProcessing, setIsProcessing] = useState(false);\n  const [error, setError] = useState<string | null>(null);\n  const [discountType] = useState<'percentage'>('percentage'); // Only percentage now\n  const [discountValue, setDiscountValue] = useState<string>('');\n  const [appliedDiscount, setAppliedDiscount] = useState<number>(0);\n  const [paymentInputs, setPaymentInputs] = useState<{ [mode: string]: string }>({});\n\n  useEffect(() => {\n    fetchPaymentModes();\n  }, [fetchPaymentModes]);\n\n  // Calculate split payment total\n  const payments = paymentModes\n    .map((mode: any) => {\n      const id = typeof mode === 'string' ? mode : mode.id;\n      const amount = parseFloat(paymentInputs[id] || '');\n      return amount > 0 ? { mode_of_payment: id, amount } : null;\n    })\n    .filter(Boolean);\n  const paymentsTotal = payments.reduce((sum, p: any) => sum + p.amount, 0);\n\n  const handleApplyDiscount = () => {\n    const value = parseFloat(discountValue);\n    if (isNaN(value) || value <= 0) {\n      setError(t('errors.invalid_discount'));\n      return;\n    }\n    if (value > 100) {\n      setError(t('errors.discount_exceeds_max'));\n      return;\n    }\n    const calculatedDiscount = (grandTotal * value) / 100;\n    setAppliedDiscount(calculatedDiscount);\n    setError(null);\n  };\n\n  // Order summary logic\n  const subtotal = grandTotal;\n  const adjustment = roundedTotal - grandTotal;\n  const roundedAdjustment = Math.round(adjustment * 100) / 100;\n  const showAdjustment = Math.abs(roundedAdjustment) > 0.001;\n  const totalDiscount = appliedDiscount;\n  const discountedTotal = Math.max(0, subtotal - totalDiscount);\n  // If discount is applied, round up; else, round normally\n  const finalTotal = appliedDiscount > 0 ? Math.ceil(discountedTotal) : Math.round(discountedTotal);\n  const finalAdjustment = finalTotal - discountedTotal;\n  const roundedFinalAdjustment = Math.round(finalAdjustment * 100) / 100;\n  const showFinalAdjustment = Math.abs(roundedFinalAdjustment) > 0.001;\n\n  useEffect(()=>{\n    const defaultPaymentModePresent=paymentModes.find((mode)=>mode===DEFAULT_PAYMENT_MODE)\n    //only one payment mode should be present, then autofill the final amount, if not do not fill\n    const otherPaymentModesNotEntered=Object.keys(paymentInputs).length<=1;\n    if(finalTotal && paymentModes && DEFAULT_PAYMENT_MODE && defaultPaymentModePresent && otherPaymentModesNotEntered){\n      //check if default payment mode is present in paymentModes\n      setPaymentInputs((prev)=>({ \n        ...prev,\n        [DEFAULT_PAYMENT_MODE]:String(finalTotal) \n      }))\n    }\n  },[finalTotal,paymentModes])\n\n  // Helper to calculate remaining balance\n  const getRemainingBalance = (currentId: string) => {\n    const totalEntered = Object.entries(paymentInputs)\n      .filter(([id]) => id !== currentId)\n      .reduce((sum, [_, val]) => sum + (parseFloat(val) || 0), 0);\n    return Math.max(0, finalTotal - totalEntered);\n  };\n\n  // Handler for input focus to auto-fill remaining balance\n  const handlePaymentInputFocus = (id: string) => {\n    setPaymentInputs(inputs => {\n      // Only auto-fill if the field is empty or zero\n      if (!inputs[id] || parseFloat(inputs[id]) === 0) {\n        const remaining = getRemainingBalance(id);\n        return { ...inputs, [id]: remaining > 0 ? String(remaining) : '' };\n      }\n      return inputs;\n    });\n  };\n\n  const handlePayment = async () => {\n    setIsProcessing(true);\n    setError(null);\n    try {\n      await call.post('ury.ury.doctype.ury_order.ury_order.make_invoice', {\n        additionalDiscount: discountValue ? parseInt(discountValue) : null,\n        cashier,\n        customer,\n        invoice,\n        owner,\n        payments,\n        pos_profile: posProfile,\n        table,\n      });\n      // Show toast and reload orders (assume showToast and reload available globally)\n      if (typeof window !== 'undefined' && (window as any).showToast) {\n        (window as any).showToast.success('Payment successful');\n      }\n      onClose();\n      clearSelectedOrder();\n      await fetchOrders();\n    } catch (err) {\n      setError((err as Error).message);\n    } finally {\n      setIsProcessing(false);\n    }\n  };\n\n  return (\n    <Dialog open={true} onOpenChange={onClose}>\n      <DialogContent variant=\"xlarge\" className=\"bg-white w-full max-w-4xl max-h-[90vh] flex flex-col md:flex-row p-0\" showCloseButton={false}>\n        {/* Left Column - Discount and Payment Mode */}\n        <div className=\"md:w-1/2 p-6 border-b md:border-b-0 md:border-r border-gray-200 overflow-y-auto\">\n          <div className=\"flex justify-between items-center mb-6\">\n            <h2 className=\"text-2xl font-bold text-gray-900\">{t('payment.title')}</h2>\n            <Button\n              onClick={onClose}\n              variant=\"ghost\"\n              size=\"icon\"\n              className=\"p-2\"\n            >\n              <X className=\"w-5 h-5\" />\n            </Button>\n          </div>\n\n          {/* Discount Section (conditional) */}\n          {storePosProfile?.enable_discount === 1 && (\n            <div className=\"space-y-4 mb-6\">\n              <h3 className=\"text-lg font-semibold flex items-center gap-2\">\n                <Percent className=\"w-5 h-5\" />\n                {t('payment.apply_discount')}\n              </h3>\n              <div className=\"flex gap-2\">\n                <Input\n                  type=\"number\"\n                  value={discountValue}\n                  onChange={(e) => setDiscountValue(e.target.value)}\n                  placeholder={t('payment.discount_placeholder')}\n                  size=\"sm\"\n                  className=\"flex-1\"\n                />\n                <Button\n                  onClick={handleApplyDiscount}\n                  variant=\"default\"\n                  size=\"sm\"\n                >\n                  {t('common.apply')}\n                </Button>\n              </div>\n            </div>\n          )}\n\n          {/* Payment Methods Section - Split Payment */}\n          <div className=\"space-y-4 mb-6\">\n            <h3 className=\"text-lg font-semibold\">{t('payment.payment_methods')}</h3>\n            <div className=\"grid grid-cols-1 gap-3\">\n              {paymentModes.map((mode: any) => {\n                const id = typeof mode === 'string' ? mode : mode.id;\n                return (\n                  <div key={id} className=\"flex items-center gap-3\">\n                    <span className=\"w-24 font-medium\">{typeof mode === 'string' ? mode : mode.name}</span>\n                    <Input\n                      type=\"number\"\n                      min=\"0\"\n                      step=\"0.01\"\n                      value={paymentInputs[id] || ''}\n                      onChange={e => setPaymentInputs(inputs => ({ ...inputs, [id]: e.target.value }))}\n                      onFocus={() => handlePaymentInputFocus(id)}\n                      placeholder={t('payment.amount_placeholder')}\n                      className=\"flex-1\"\n                      size=\"sm\"\n                      disabled={isProcessing}\n                    />\n                  </div>\n                );\n              })}\n            </div>\n            <div className=\"flex justify-between mt-2 text-sm\">\n              <span className=\"font-medium\">{t('payment.total_entered')}</span>\n              <span className={'text-green-600 font-semibold flex items-center gap-1'}>\n                {formatCurrency(paymentsTotal)} / {formatCurrency(finalTotal)}\n                {paymentsTotal > finalTotal && (\n                  <span className=\"text-yellow-700 font-semibold\">\n                    <Coins className=\"inline w-4 h-4 ml-1 text-yellow-500\" />\n                    <span className=\"text-yellow-500 font-bold ml-1\">{formatCurrency(paymentsTotal - finalTotal)}</span>\n                  </span>\n                )}\n              </span>\n            </div>\n          </div>\n        </div>\n\n        {/* Right Column - Order Summary and Pay Button */}\n        <div className=\"md:w-1/2 p-6 overflow-y-auto\">\n          {/* Error Message */}\n          {error && (\n            <div className=\"mb-4 p-3 bg-red-50 border border-red-200 rounded-lg\">\n              <p className=\"text-red-700 text-sm\">{error}</p>\n            </div>\n          )}\n\n          {/* Order Summary */}\n          <div className=\"space-y-3 mb-6\">\n            <h3 className=\"text-lg font-semibold\">{t('payment.order_summary')}</h3>\n            <div className=\"space-y-2 text-sm\">\n              {/* Subtotal (Grand Total) */}\n              <div className=\"flex justify-between\">\n                <span className=\"text-gray-600\">{t('payment.subtotal')}</span>\n                <span>{formatCurrency(subtotal)}</span>\n              </div>\n              {/* Discount */}\n              {appliedDiscount > 0 && (\n                <div className=\"flex justify-between text-green-600\">\n                  <span>{t('payment.discount')}</span>\n                  <span>-{formatCurrency(appliedDiscount)}</span>\n                </div>\n              )}\n              {/* Adjustment (if any) */}\n              {showFinalAdjustment && (\n                <div className=\"flex justify-between text-blue-600\">\n                  <span>{t('payment.adjustment')}</span>\n                  <span>{roundedFinalAdjustment > 0 ? '+' : ''}{formatCurrency(roundedFinalAdjustment)}</span>\n                </div>\n              )}\n              {/* Final Total (Rounded) */}\n              <div className=\"border-t pt-2\">\n                <div className=\"flex justify-between font-semibold text-lg\">\n                  <span>{t('payment.total')}</span>\n                  <span>{formatCurrency(finalTotal)}</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          {/* Payment Button */}\n          <Button\n            onClick={handlePayment}\n            disabled={isProcessing || payments.length === 0}\n            variant={isProcessing || payments.length === 0 ? \"secondary\" : \"default\"}\n            className=\"w-full\"\n          >\n            {isProcessing ? t('payment.processing') : t('payment.pay_button', { amount: formatCurrency(paymentsTotal > 0 ? paymentsTotal : finalTotal) })}\n          </Button>\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nexport default PaymentDialog; "
  },
  {
    "path": "pos/src/components/ProductDialog.tsx",
    "content": "import React, { useState, useEffect, useRef, ChangeEvent } from 'react';\nimport { X, Plus, Minus } from 'lucide-react';\nimport { OrderItem, usePOSStore } from '../store/pos-store';\nimport { cn, formatCurrency } from '../lib/utils';\nimport { Button, Dialog, DialogContent, Input } from './ui';\nimport { db } from '../lib/frappe-sdk';\nimport { t } from '../i18n';\n\ninterface Variant {\n  id: string;\n  name: string;\n  price: number;\n}\n\ninterface Addon {\n  id: string;\n  name: string;\n  price: number;\n  category: 'sides' | 'drinks' | 'desserts';\n}\n\ninterface ProductDialogProps {\n  onClose: () => void;\n  editMode?: boolean;\n  initialVariant?: Variant;\n  initialAddons?: Array<Omit<Addon, 'category'>>;\n  initialQuantity?: number;\n  itemToReplace?: OrderItem;\n}\n\nconst ProductDialog: React.FC<ProductDialogProps> = ({\n  onClose,\n  editMode = false,\n  initialVariant,\n  initialAddons = [],\n  initialQuantity,\n  itemToReplace\n}) => {\n  const { \n    selectedItem, \n    addToOrder, \n    removeFromOrder, \n    setSelectedItem, \n    getItemQuantityFromCart,\n    activeOrders,\n    menuItems\n  } = usePOSStore();\n  \n  // Find existing item in cart\n  const existingCartItem = selectedItem ? activeOrders.find(\n    order => order.id === selectedItem.id &&\n    (!order.selectedVariant || order.selectedVariant.id === initialVariant?.id) &&\n    (!order.selectedAddons || order.selectedAddons.length === initialAddons.length && \n      order.selectedAddons.every(addon => \n        initialAddons.some(initAddon => initAddon.id === addon.id)\n      ))\n  ) : null;\n\n  // State for the full item doc (used for all dialog content)\n  const [itemDoc, setItemDoc] = useState<any | null>(null);\n  const [isItemLoading, setIsItemLoading] = useState(false);\n  const [itemError, setItemError] = useState<string | null>(null);\n\n  // Fetch Item doc when dialog opens or selectedItem changes\n  useEffect(() => {\n    if (!selectedItem) {\n      setItemDoc(null);\n      setItemError(null);\n      setIsItemLoading(false);\n      return;\n    }\n    setIsItemLoading(true);\n    setItemError(null);\n    db.getDoc('Item', selectedItem.item)\n      .then((doc: any) => {\n        setItemDoc(doc);\n      })\n      .catch(() => {\n        setItemError('Failed to fetch item details');\n        setItemDoc(null);\n      })\n      .finally(() => {\n        setIsItemLoading(false);\n      });\n  }, [selectedItem]);\n\n  \n  const addonDetails = Array.isArray(itemDoc?.custom_pos_add_on_items)\n    ? itemDoc.custom_pos_add_on_items\n        .map((entry: any) => {\n          const menuAddon = menuItems.find((menuItem: any) => menuItem.item === entry.item);\n          return menuAddon\n            ? {\n                id: menuAddon.item,\n                name: menuAddon.item_name,\n                price: Number(menuAddon.price)\n              }\n            : {\n                id: entry.item,\n                name: entry.item,\n                price: 0\n              };\n        })\n        .filter(Boolean)\n    : [];\n\n  const variantDetails = Array.isArray(itemDoc?.custom_pos_item_variants)\n    ? itemDoc.custom_pos_item_variants\n        .map((entry: any) => {\n          const menuVariant = menuItems.find((menuItem: any) => menuItem.item === entry.item);\n          return menuVariant\n            ? {\n                id: menuVariant.item,\n                name: menuVariant.item_name,\n                price: Number(menuVariant.price)\n              }\n            : {\n                id: entry.item,\n                name: entry.item,\n                price: 0\n              };\n        })\n        .filter(Boolean)\n    : [];\n\n  const [selectedAddons, setSelectedAddons] = useState<Array<{ id: string; name: string; price: number }>>([]);\n  const [quantity, setQuantity] = useState<string>(editMode ? initialQuantity?.toString() || '0' : '0');\n  const [comments, setComments] = useState<string>(itemToReplace?.comment || existingCartItem?.comment || '');\n  const dialogRef = useRef<HTMLDivElement>(null);\n\n  const [addonItemCodes, setAddonItemCodes] = useState<string[]>([]);\n  const [isAddonLoading, setIsAddonLoading] = useState(false);\n  const [addonError, setAddonError] = useState<string | null>(null);\n\n  useEffect(() => {\n    if (!selectedItem) {\n      setAddonItemCodes([]);\n      setAddonError(null);\n      setIsAddonLoading(false);\n      return;\n    }\n    setIsAddonLoading(true);\n    setAddonError(null);\n    db.getDoc('Item', selectedItem.item)\n      .then((doc: any) => {\n        if (Array.isArray(doc.custom_pos_add_on_items)) {\n          const codes = doc.custom_pos_add_on_items\n            .map((entry: any) => entry.item)\n            .filter(Boolean);\n          setAddonItemCodes(codes);\n        } else {\n          setAddonItemCodes([]);\n        }\n      })\n      .catch((err: any) => {\n        setAddonError('Failed to fetch add-ons');\n        setAddonItemCodes([]);\n      })\n      .finally(() => {\n        setIsAddonLoading(false);\n      });\n  }, [selectedItem]);\n\n  // Initialize quantity and comments from cart if not in edit mode\n  useEffect(() => {\n    if (!editMode && selectedItem) {\n      if (existingCartItem) {\n        setQuantity(existingCartItem.quantity.toString());\n        setComments(existingCartItem.comment || '');\n      } else {\n        const cartQuantity = getItemQuantityFromCart(selectedItem);\n        setQuantity(cartQuantity.toString());\n      }\n    }\n  }, [selectedItem, editMode, getItemQuantityFromCart, existingCartItem]);\n\n  // Handle click outside to close dialog\n  useEffect(() => {\n    const handleClickOutside = (event: MouseEvent) => {\n      if (dialogRef.current && !dialogRef.current.contains(event.target as Node)) {\n        handleClose();\n      }\n    };\n\n    document.addEventListener('mousedown', handleClickOutside);\n    return () => {\n      document.removeEventListener('mousedown', handleClickOutside);\n    };\n  }, []);\n\n  // Handle escape key to close dialog\n  useEffect(() => {\n    const handleEscape = (event: KeyboardEvent) => {\n      if (event.key === 'Escape') {\n        handleClose();\n      }\n    };\n\n    document.addEventListener('keydown', handleEscape);\n    return () => {\n      document.removeEventListener('keydown', handleEscape);\n    };\n  }, []);\n\n  if (!selectedItem) return null;\n\n  // Always get price from menuItems for the main item\n  const basePrice = selectedItem?.price ? Number(selectedItem.price) : 0;\n  const numericQuantity = quantity === '' ? 0 : parseInt(quantity, 10);\n  const addonsTotal = selectedAddons.reduce((sum, addon) => sum + addon.price, 0);\n  const total = (basePrice + addonsTotal) * numericQuantity;\n\n  const handleQuantityChange = (value: string) => {\n    // Allow empty string or numbers\n    if (value === '') {\n      setQuantity('');\n      return;\n    }\n\n    const num = parseInt(value, 10);\n    if (!isNaN(num) && num >= 0 && num <= 99) {\n      setQuantity(num.toString());\n    }\n  };\n\n  const handleIncrement = () => {\n    const currentNum = quantity === '' ? 0 : parseInt(quantity, 10);\n    if (currentNum < 99) {\n      setQuantity((currentNum + 1).toString());\n    }\n  };\n\n  const handleDecrement = () => {\n    const currentNum = quantity === '' ? 0 : parseInt(quantity, 10);\n    if (currentNum > 0) {\n      setQuantity((currentNum - 1).toString());\n    }\n  };\n\n  const handleAddToOrder = () => {\n    const numericQuantity = typeof quantity === 'string' ? parseInt(quantity, 10) : quantity;\n    if (isNaN(numericQuantity) || numericQuantity === 0) {\n      return; // Don't add to order if quantity is 0 or invalid\n    }\n\n    if (editMode && itemToReplace?.uniqueId) {\n      // Remove the old item first\n      removeFromOrder(itemToReplace.uniqueId);\n    }\n\n    // Add main item as a cart line\n    const orderItem: OrderItem = {\n      ...selectedItem,\n      quantity: numericQuantity,\n      price: basePrice\n    };\n    addToOrder(orderItem);\n\n    // Add each selected add-on as a separate cart line\n    selectedAddons.forEach(addon => {\n      // Find the full menu item details for the add-on\n      const menuAddon = menuItems.find(item => item.item === addon.id);\n      const addonOrderItem: OrderItem = menuAddon\n        ? {\n            ...menuAddon,\n            quantity: numericQuantity,\n            price: addon.price\n          }\n        : {\n            id: addon.id,\n            name: addon.name,\n            price: addon.price,\n            quantity: numericQuantity,\n            image: null,\n            item: addon.id,\n            item_name: addon.name,\n            course: '',\n            description: '',\n            special_dish: 0 as 0 | 1,\n            tax_rate: 0\n          } as OrderItem;\n      addToOrder(addonOrderItem);\n    });\n\n    handleClose();\n  };\n\n  const handleClose = () => {\n    setSelectedItem(null);\n    onClose();\n  };\n\n  const handleAddonToggle = (addon: Omit<Addon, 'category'>) => {\n    setSelectedAddons(current => \n      current.some(item => item.id === addon.id)\n        ? current.filter(item => item.id !== addon.id)\n        : [...current, addon]\n    );\n  };\n\n  // Handler to switch to a variant item\n  const handleVariantClick = (variantId: string) => {\n    const menuVariant = menuItems.find((m: any) => m.item === variantId);\n    if (menuVariant) {\n      setSelectedItem(menuVariant);\n    }\n  };\n\n  return (\n    <Dialog open={true} onOpenChange={handleClose}>\n      <DialogContent \n        ref={dialogRef}\n        variant=\"xlarge\"\n        className=\"bg-white w-full max-w-[90rem] max-h-[90vh] overflow-y-auto flex flex-col md:flex-row p-0\"\n        showCloseButton={false}\n      >\n        {/* Left Column - Image  */}\n        <div className=\"md:w-1/3 relative\">\n          {itemDoc?.image ? (\n            <img\n              src={itemDoc.image}\n              alt={itemDoc.name}\n              className=\"w-full min-h-96 h-full object-cover rounded-t-lg md:rounded-l-lg md:rounded-tr-none filter saturate-75 brightness-95\"\n              style={{ filter: 'saturate(0.7) brightness(0.95)' }}\n              onError={(e) => {\n                const target = e.target as HTMLImageElement;\n                target.style.display = 'none';\n                const parent = target.parentElement;\n                if (parent) {\n                  const placeholder = document.createElement('div');\n                  placeholder.className = 'w-full h-96 bg-gray-200 flex items-center justify-center text-[8rem] text-gray-400 font-medium rounded-t-lg md:rounded-l-lg md:rounded-tr-none';\n                  placeholder.textContent = itemDoc.name.slice(0, 2).toUpperCase();\n                  parent.insertBefore(placeholder, target);\n                }\n              }}\n            />\n          ) : (\n            <div className=\"w-full min-h-96 h-full bg-gray-200 flex items-center justify-center text-[8rem] text-gray-400 font-medium rounded-t-lg md:rounded-l-lg md:rounded-tr-none\">\n              {itemDoc?.name.slice(0, 2).toUpperCase()}\n            </div>\n          )}\n          <Button\n            onClick={handleClose}\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"absolute top-4 right-4 bg-white shadow-lg\"\n          >\n            <X className=\"w-5 h-5\" />\n          </Button>\n        </div>\n\n        {/* Middle Column - Variants and Quantity */}\n        <div className=\"md:w-1/3 p-6 overflow-y-auto\">\n          <div>\n            <h2 className=\"text-2xl font-bold text-gray-900\">{selectedItem?.item_name}</h2>\n            <div className=\"flex items-center gap-2 mt-1\">\n              <span className=\"text-sm text-gray-500\">{selectedItem?.item}</span>\n              {(selectedItem?.course_label || selectedItem?.course) && (\n                <>\n                  <span className=\"text-gray-300\">•</span>\n                  <span className=\"text-sm font-medium text-blue-600\">{selectedItem?.course_label || selectedItem?.course}</span>\n                </>\n              )}\n            </div>\n          </div>\n\n          <div className=\"mt-6\">\n            <h3 className=\"text-lg font-semibold mb-3\">{t('product_dialog.special_instructions')}</h3>\n            <Input\n              placeholder={t('product_dialog.special_instructions_placeholder')}\n              value={comments}\n              onChange={(e: ChangeEvent<HTMLInputElement>) => setComments(e.target.value)}\n              className=\"resize-none\"\n            />\n          </div>\n\n          <div className=\"mt-6\">\n            <h3 className=\"text-lg font-semibold mb-3\">{t('product_dialog.quantity')}</h3>\n            <div className=\"flex items-center space-x-2\">\n              <Button\n                onClick={handleDecrement}\n                variant=\"outline\"\n                size=\"icon\"\n                className=\"h-8 w-8 rounded-full\"\n              >\n                <Minus className=\"h-4 w-4\" />\n              </Button>\n              <Input\n                type=\"number\"\n                min=\"0\"\n                max=\"99\"\n                value={quantity}\n                onChange={(e) => handleQuantityChange(e.target.value)}\n                onBlur={() => {\n                  // If empty on blur, set to 0\n                  if (quantity === '') {\n                    setQuantity('0');\n                  }\n                }}\n                className=\"w-16 text-center [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none\"\n              />\n              <Button\n                onClick={handleIncrement}\n                variant=\"outline\"\n                size=\"icon\"\n                className=\"h-8 w-8 rounded-full\"\n              >\n                <Plus className=\"h-4 w-4\" />\n              </Button>\n            </div>\n          </div>\n          {/* Variants Section  */}\n          {variantDetails.length > 0 && (\n            <div className=\"mt-6\">\n              <h3 className=\"text-lg font-semibold mb-3\">{t('product_dialog.variants')}</h3>\n              <div className=\"flex gap-2 flex-wrap\">\n                {variantDetails.map((variant: any) => {\n                  const menuVariant = menuItems.find((m: any) => m.item === variant.id);\n                  return (\n                    <button\n                      key={variant.id}\n                      onClick={() => handleVariantClick(variant.id)}\n                      className={cn(\n                        'p-2 rounded-lg border text-left w-full flex justify-between items-center',\n                        variant.id === itemDoc?.item\n                          ? 'border-blue-500 bg-blue-50'\n                          : 'border-gray-200 hover:border-blue-200'\n                      )}\n                    >\n                      <div className=\"font-medium\">{variant.name}</div>\n                      <div className=\"text-sm text-gray-500\">{formatCurrency(menuVariant ? Number(menuVariant.price) : 0)}</div>\n                    </button>\n                  );\n                })}\n              </div>\n            </div>\n          )}\n        </div>\n\n\n        {/* Right Column - Add-ons and Order Button */}\n        <div className=\"h-auto md:w-1/3 p-6 border-t md:border-t-0 md:border-l border-gray-200 overflow-y-auto flex flex-col\">\n          <div className=\"overflow-y-auto mb-6\">\n            {isAddonLoading ? (\n              <div className=\"mb-6 flex items-center justify-center text-gray-500\">{t('product_dialog.loading_addons')}</div>\n            ) : addonError ? (\n              <div className=\"flex items-center justify-center text-red-500\">{addonError}</div>\n            ) : addonDetails.length > 0 ? (\n              <div className=\"mb-6\">\n                <h3 className=\"text-lg font-semibold mb-3\">{t('product_dialog.addons')}</h3>\n                <div className=\"space-y-2\">\n                  {addonDetails.map((addon: any) => (\n                    <button\n                      key={addon.id}\n                      onClick={() => handleAddonToggle({ id: addon.id, name: addon.name, price: Number(addon.price) })}\n                      className={cn(\n                        'w-full p-3 rounded-lg border text-left',\n                        selectedAddons.some(item => item.id === addon.id)\n                          ? 'border-blue-500 bg-blue-50'\n                          : 'border-gray-200 hover:border-blue-200'\n                      )}\n                    >\n                      <div className=\"flex justify-between items-center\">\n                        <span>{addon.name}</span>\n                        <span className=\"text-sm text-gray-500\">+{formatCurrency(Number(addon.price))}</span>\n                      </div>\n                    </button>\n                  ))}\n                </div>\n              </div>\n            ) : (\n              <div className=\"flex items-center justify-center text-gray-400 text-sm\">{t('product_dialog.no_addons')}</div>\n            )}\n          </div>\n          {/* Always show total section at the end */}\n          <div className=\"mt-auto pt-2 border-t border-gray-200\">\n            <div className=\"flex justify-between items-center text-lg font-semibold\">\n              <span>{t('product_dialog.total')}&nbsp;</span>\n              <span>{formatCurrency(total)}</span>\n            </div>\n            <Button\n              onClick={handleAddToOrder}\n              className=\"w-full mt-4\"\n              size=\"lg\"\n              disabled={numericQuantity === 0}\n            >\n              {editMode || existingCartItem ? t('product_dialog.update_order') : t('product_dialog.add_to_order')}\n            </Button>\n          </div>\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nexport default ProductDialog; "
  },
  {
    "path": "pos/src/components/ScreenSizeDialog.tsx",
    "content": "import { Monitor, Smartphone, ExternalLink } from 'lucide-react';\nimport { Button } from './ui';\n\nconst ScreenSizeDialog = () => {\n  const handleSwitchToVersion1 = () => {\n    // Get the current domain and open /urypos in a new tab\n    const currentDomain = window.location.origin;\n    window.open(`${currentDomain}/urypos`, '_blank');\n  };\n\n  return (\n    <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\n      <div className=\"bg-white rounded-lg p-8 max-w-md w-full mx-4 shadow-xl\">\n        <div className=\"text-center\">\n          {/* Icon */}\n          <div className=\"mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-blue-100 mb-6\">\n            <div className=\"relative\">\n              <Monitor className=\"h-8 w-8 text-blue-600\" />\n              <Smartphone className=\"h-4 w-4 text-red-500 absolute -top-1 -right-1\" />\n            </div>\n          </div>\n          \n          {/* Title */}\n          <h2 className=\"text-2xl font-bold text-gray-900 mb-4\">\n            Desktop Only\n          </h2>\n          \n          {/* Message */}\n          <div className=\"text-gray-600 mb-8 space-y-3\">\n            <p className=\"text-lg\">\n              This POS system is designed for desktop computers and tablets with larger screens.\n            </p>\n            <p className=\"text-sm\">\n              Mobile support will be available in a future update. Please use a device with a screen width of 1024px or larger.\n            </p>\n          </div>\n          \n          {/* Current Screen Info */}\n          <div className=\"bg-gray-50 rounded-lg p-4 mb-6\">\n            <p className=\"text-sm text-gray-600\">\n              Current screen width: <span className=\"font-semibold text-gray-800\">{window.innerWidth}px</span>\n            </p>\n            <p className=\"text-sm text-gray-600\">\n              Required: <span className=\"font-semibold text-gray-800\">1024px or larger</span>\n            </p>\n          </div>\n          \n          {/* Alternative Option */}\n          <div className=\"bg-blue-50 rounded-lg p-4 mb-6\">\n            <p className=\"text-sm text-blue-800 mb-3\">\n              You can use URY POS Version 1 for mobile devices.\n            </p>\n            <Button\n              onClick={handleSwitchToVersion1}\n              className=\"w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 text-sm\"\n            >\n              <ExternalLink className=\"w-4 h-4 mr-2\" />\n              Switch to Version 1\n            </Button>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default ScreenSizeDialog; "
  },
  {
    "path": "pos/src/components/ScreenSizeProvider.tsx",
    "content": "import { useState, useEffect } from 'react';\nimport ScreenSizeDialog from './ScreenSizeDialog';\n\ninterface ScreenSizeProviderProps {\n  children: React.ReactNode;\n}\n\nconst ScreenSizeProvider = ({ children }: ScreenSizeProviderProps) => {\n  const [isScreenTooSmall, setIsScreenTooSmall] = useState(false);\n\n  const checkScreenSize = () => {\n    const isSmall = window.innerWidth < 1024;\n    setIsScreenTooSmall(isSmall);\n  };\n\n  useEffect(() => {\n    // Check on mount\n    checkScreenSize();\n\n    // Add resize listener\n    const handleResize = () => {\n      checkScreenSize();\n    };\n\n    window.addEventListener('resize', handleResize);\n\n    // Cleanup\n    return () => {\n      window.removeEventListener('resize', handleResize);\n    };\n  }, []);\n\n  // Show dialog if screen is too small\n  if (isScreenTooSmall) {\n    return <ScreenSizeDialog />;\n  }\n\n  // Render children if screen size is acceptable\n  return <>{children}</>;\n};\n\nexport default ScreenSizeProvider; "
  },
  {
    "path": "pos/src/components/SearchBar.tsx",
    "content": "import React, { useRef, useEffect } from 'react';\nimport { Search, X } from 'lucide-react';\nimport { cn } from '../lib/utils';\n\ninterface SearchBarProps {\n  value: string;\n  onChange: (value: string) => void;\n  onVisibilityChange: (isVisible: boolean) => void;\n  isVisible: boolean;\n  disabled?: boolean;\n}\n\nexport default function SearchBar({\n  value,\n  onChange,\n  onVisibilityChange,\n  isVisible,\n  disabled\n}: SearchBarProps) {\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    if (isVisible && inputRef.current) {\n      inputRef.current.focus();\n    }\n  }, [isVisible]);\n\n  return (\n    <div className=\"flex items-center\">\n      <button\n        onClick={() => onVisibilityChange(true)}\n        className={cn(\n          'h-9 flex items-center gap-2 px-3 rounded-full text-sm font-medium transition-all duration-200 ease-in-out hover:scale-105 active:scale-95',\n          isVisible ? 'hidden' : 'bg-gray-100 text-gray-700 hover:bg-gray-200',\n          disabled && 'opacity-50 cursor-not-allowed pointer-events-none'\n        )}\n        disabled={disabled}\n      >\n        <Search className=\"w-4 h-4\" />\n      </button>\n\n      <div \n        className={cn(\n          'transition-all duration-200 ease-in-out',\n          isVisible \n            ? 'w-56 opacity-100' \n            : 'w-0 opacity-0',\n          disabled && 'opacity-50'\n        )}\n      >\n        <div className={cn(\n          'relative h-9',\n          !isVisible && 'invisible'\n        )}>\n          <input\n            ref={inputRef}\n            type=\"text\"\n            placeholder=\"Search menu items...\"\n            className={cn(\n              \"w-full h-full border border-gray-200 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-primary-100 focus:border-primary-500 px-[12px] py-[8px] transition-all duration-200\",\n              disabled && \"bg-gray-50 cursor-not-allowed\"\n            )}\n            value={value}\n            onChange={(e) => onChange(e.target.value)}\n            disabled={disabled}\n          />\n          <button\n            onClick={() => {\n              onVisibilityChange(false);\n              onChange('');\n            }}\n            className={cn(\n              \"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-all duration-200 hover:scale-110 active:scale-90\",\n              disabled && \"opacity-50 cursor-not-allowed pointer-events-none\"\n            )}\n            disabled={disabled}\n          >\n            <X className=\"w-4 h-4\" />\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n} "
  },
  {
    "path": "pos/src/components/Sidebar.tsx",
    "content": "import { \n  Grid3X3,\n  UtensilsCrossed,\n} from 'lucide-react';\nimport { usePOSStore } from '../store/pos-store';\nimport { cn } from '../lib/utils';\nimport { Button, Badge } from './ui';\nimport CommentDialog from './CommentDialog';\nimport { useState } from 'react';\nimport { t } from '../i18n';\n\ninterface SidebarProps {\n  disabled?: boolean;\n}\n\nconst Sidebar = ({ disabled }: SidebarProps) => {\n  const { selectedCategory, setSelectedCategory, menuItems, categories, orderComment, setOrderComment } = usePOSStore();\n  const [showCommentDialog, setShowCommentDialog] = useState(false);\n\n  // Count items per category\n  const getCategoryCount = (category: string) => {\n    const count = menuItems.filter(item => item.course === category).length;\n    return count;\n  };\n\n  const getAllItemsCount = () => {\n    const count = menuItems.length;\n    return count;\n  };\n\n  const handleCommentSave = (comment: string) => {\n    setOrderComment(comment);\n  };\n\n  return (\n    <div className={cn(\n      \"w-64 bg-white border-e border-gray-200 h-screen flex flex-col\",\n      disabled && \"opacity-50 pointer-events-none\"\n    )}>\n      {/* Categories List */}\n      <nav className=\"flex-1 p-6 overflow-y-auto\">\n        <div className=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n          {/* Section Title */}\n          <h2 className=\"text-xs font-medium text-gray-500 uppercase tracking-wide mb-3 px-1\">\n            {t('pos_sidebar.categories')}\n          </h2>\n          \n          {/* All Items */}\n          <Button\n            onClick={() => setSelectedCategory('')}\n            variant=\"ghost\"\n            className={cn(\n              'w-full flex items-center justify-between px-3 py-2.5 text-sm font-medium transition-all duration-200 group relative mb-1',\n              selectedCategory === ''\n                ? 'bg-white text-gray-900 shadow-sm font-semibold'\n                : 'text-gray-700 hover:bg-white/60 hover:text-gray-900'\n            )}\n            disabled={disabled}\n          >\n            {/* Active indicator bar */}\n            {selectedCategory === '' && (\n              <div className=\"absolute start-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-blue-600 rounded-e-full\" />\n            )}\n            \n            <div className=\"flex items-center gap-3 ms-1\">\n              <Grid3X3 className=\"w-4 h-4 text-gray-500\" />\n              <span>{t('pos_sidebar.all_items')}</span>\n            </div>\n            \n            <Badge variant=\"secondary\" size=\"sm\" className=\"text-xs text-gray-500 bg-gray-100 min-w-[24px] text-center\">\n              {getAllItemsCount()}\n            </Badge>\n          </Button>\n\n          {/* Divider */}\n          <div className=\"h-px bg-gray-200 my-3 mx-1\" />\n\n          {/* Category Items */}\n          <div className=\"space-y-1\">\n            {categories.map((category) => {\n              const count = getCategoryCount(category.name);\n              return (\n                <Button\n                  key={category.name}\n                  onClick={() => setSelectedCategory(category.name)}\n                  variant=\"ghost\"\n                  className={cn(\n                    'w-full flex items-center justify-between px-3 py-2.5 text-sm font-medium transition-all duration-200 group relative',\n                    selectedCategory === category.name\n                      ? 'bg-white text-gray-900 shadow-sm font-semibold'\n                      : 'text-gray-700 hover:bg-white/60 hover:text-gray-900'\n                  )}\n                  disabled={disabled}\n                >\n                  {/* Active indicator bar */}\n                  {selectedCategory === category.name && (\n                    <div className=\"absolute start-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-blue-600 rounded-e-full\" />\n                  )}\n                  <div className=\"flex items-center gap-3 ms-1\">\n                    <UtensilsCrossed className=\"w-4 h-4 text-gray-500 flex-shrink-0\" />\n                    <span className=\"text-start\">{category.label}</span>\n                  </div>\n                  <Badge variant=\"secondary\" size=\"sm\" className=\"text-xs text-gray-500 bg-gray-100 min-w-[24px] text-center\">\n                    {count}\n                  </Badge>\n                </Button>\n              );\n            })}\n          </div>\n        </div>\n      </nav>\n\n      {/* Comment Dialog is rendered from sidebar but triggered from order panel, to not mount it on every order panel render */}\n      <CommentDialog\n        isOpen={showCommentDialog}\n        onClose={() => setShowCommentDialog(false)}\n        onSave={handleCommentSave}\n        initialComment={orderComment}\n      />\n    </div>\n  );\n};\n\nexport default Sidebar; "
  },
  {
    "path": "pos/src/components/Spotlight.tsx",
    "content": "//Not currently used, will update later\nimport { useState, useEffect, useRef } from 'react';\nimport { Search, Command, X } from 'lucide-react';\nimport { usePOSStore } from '../store/pos-store';\nimport { cn, formatCurrency } from '../lib/utils';\nimport { Button, Input } from './ui';\nimport { Dialog, DialogContent } from './ui/dialog';\n\nconst Spotlight = () => {\n  const [isOpen, setIsOpen] = useState(false);\n  const [query, setQuery] = useState('');\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const { menuItems, addToOrder, setSelectedItem } = usePOSStore();\n\n  const filteredItems = menuItems.filter(item =>\n    item.name.toLowerCase().includes(query.toLowerCase()) ||\n    item.category.toLowerCase().includes(query.toLowerCase())\n  ).slice(0, 10);\n\n  useEffect(() => {\n    const handleKeyDown = (e: KeyboardEvent) => {\n      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {\n        e.preventDefault();\n        setIsOpen(true);\n      }\n      if (e.key === 'Escape') {\n        setIsOpen(false);\n      }\n    };\n\n    document.addEventListener('keydown', handleKeyDown);\n    return () => document.removeEventListener('keydown', handleKeyDown);\n  }, []);\n\n  useEffect(() => {\n    if (isOpen && inputRef.current) {\n      inputRef.current.focus();\n    }\n  }, [isOpen]);\n\n  const handleSelect = (item: typeof menuItems[0]) => {\n    setSelectedItem(item);\n    setIsOpen(false);\n    setQuery('');\n    setSelectedIndex(0);\n  };\n\n  const handleKeyDown = (e: React.KeyboardEvent) => {\n    if (e.key === 'ArrowDown') {\n      e.preventDefault();\n      setSelectedIndex(prev => Math.min(prev + 1, filteredItems.length - 1));\n    } else if (e.key === 'ArrowUp') {\n      e.preventDefault();\n      setSelectedIndex(prev => Math.max(prev - 1, 0));\n    } else if (e.key === 'Enter' && filteredItems[selectedIndex]) {\n      e.preventDefault();\n      handleSelect(filteredItems[selectedIndex]);\n    }\n  };\n\n  return (\n    <Dialog open={isOpen} onOpenChange={setIsOpen}>\n      <DialogContent className=\"bg-white rounded-lg shadow-xl w-full max-w-2xl p-0\">\n        <div className=\"flex items-center border-b border-gray-200 p-4\">\n          <Search className=\"w-5 h-5 text-gray-400 me-3\" />\n          <Input\n            ref={inputRef}\n            type=\"text\"\n            placeholder=\"Search menu items...\"\n            className=\"flex-1 outline-none text-lg border-0 shadow-none focus:ring-0\"\n            value={query}\n            onChange={(e) => setQuery(e.target.value)}\n            onKeyDown={handleKeyDown}\n          />\n          <Button\n            onClick={() => setIsOpen(false)}\n            variant=\"ghost\"\n            size=\"icon\"\n            className=\"ms-3\"\n          >\n            <X className=\"w-5 h-5\" />\n          </Button>\n        </div>\n\n        <div className=\"max-h-96 overflow-y-auto\">\n          {filteredItems.length > 0 ? (\n            filteredItems.map((item, index) => (\n              <Button\n                key={item.id}\n                onClick={() => handleSelect(item)}\n                variant=\"ghost\"\n                className={cn(\n                  'w-full flex items-center p-4 hover:bg-gray-50 transition-colors',\n                  index === selectedIndex && 'bg-blue-50'\n                )}\n              >\n                <img\n                  src={item.image}\n                  alt={item.name}\n                  className=\"w-12 h-12 object-cover rounded me-4\"\n                />\n                <div className=\"flex-1 text-start\">\n                  <div className=\"font-medium\">{item.name}</div>\n                  <div className=\"text-sm text-gray-500\">{item.category}</div>\n                </div>\n                <div className=\"text-end\">\n                  <div className=\"font-medium\">{formatCurrency(item.price)}</div>\n                </div>\n              </Button>\n            ))\n          ) : (\n            <div className=\"p-4 text-center text-gray-500\">\n              No items found\n            </div>\n          )}\n        </div>\n\n        <div className=\"border-t border-gray-200 p-4 text-sm text-gray-500\">\n          <div className=\"flex items-center justify-between\">\n            <span>Use ↑↓ to navigate, Enter to select</span>\n            <div className=\"flex items-center gap-2\">\n              <Command className=\"w-4 h-4\" />\n              <span>K</span>\n            </div>\n          </div>\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n};\nexport default Spotlight; \n"
  },
  {
    "path": "pos/src/components/TableSelectionDialog.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { X, Square, AlertTriangle } from 'lucide-react';\nimport { usePOSStore } from '../store/pos-store';\nimport { Dialog, DialogContent } from './ui/dialog';\nimport { Button } from './ui/button';\nimport { cn } from '../lib/utils';\nimport { getRooms, getTables, Room, Table } from '../lib/table-api';\nimport { Badge } from './ui/badge';\nimport { Spinner } from './ui/spinner';\nimport { TableShapeIcon } from './TableShapeIcon';\nimport { t } from '../i18n';\n\ninterface Props {\n  onClose: () => void;\n}\n\nconst TableSelectionDialog: React.FC<Props> = ({ onClose }) => {\n  const { selectedTable, setSelectedTable, posProfile } = usePOSStore();\n  const [rooms, setRooms] = useState<Room[]>([]);\n  const [tables, setTables] = useState<Table[]>([]);\n  const [tablesCache, setTablesCache] = useState<Record<string, Table[]>>({});\n  const [selectedRoom, setSelectedRoom] = useState<string | null>(null);\n  const [loadingRooms, setLoadingRooms] = useState(false);\n  const [loadingTables, setLoadingTables] = useState(false);\n  const [error, setError] = useState<string | null>(null);\n\n  const sortTables = (tables: Table[]): Table[] => {\n    return [...tables].sort((a, b) => a.name.localeCompare(b.name));\n  };\n\n  // Fetch rooms on mount with session storage\n  useEffect(() => {\n    async function fetchRooms() {\n      if (!posProfile?.branch) return;\n      setLoadingRooms(true);\n      setError(null);\n\n      try {\n        // Try to get rooms from session storage first\n        const sessionKey = `ury_rooms_${posProfile.branch}`;\n        const cachedRooms = sessionStorage.getItem(sessionKey);\n        \n        if (cachedRooms) {\n          const parsedRooms = JSON.parse(cachedRooms) as Room[];\n          setRooms(parsedRooms);\n          if (parsedRooms.length > 0) {\n            setSelectedRoom(parsedRooms[0].name);\n          }\n        } else {\n          // If not in session storage, fetch from API\n          const fetchedRooms = await getRooms(posProfile.branch);\n          setRooms(fetchedRooms);\n          if (fetchedRooms.length > 0) {\n            setSelectedRoom(fetchedRooms[0].name);\n          }\n          // Store in session storage\n          sessionStorage.setItem(sessionKey, JSON.stringify(fetchedRooms));\n        }\n      } catch (e) {\n        setError(t('errors.failed_load_rooms') || 'Failed to load rooms');\n      } finally {\n        setLoadingRooms(false);\n      }\n    }\n    fetchRooms();\n  }, [posProfile?.branch]);\n\n  // Fetch tables when selectedRoom changes, but cache per room\n  useEffect(() => {\n    async function fetchTables() {\n      if (!selectedRoom) return;\n      setError(null);\n      // If already cached, use cache\n      if (tablesCache[selectedRoom]) {\n        setTables(sortTables(tablesCache[selectedRoom]));\n        setLoadingTables(false);\n        return;\n      }\n      setLoadingTables(true);\n      try {\n        const fetchedTables = await getTables(selectedRoom);\n        const sortedTables = sortTables(fetchedTables);\n        setTables(sortedTables);\n        setTablesCache(prev => ({ ...prev, [selectedRoom]: fetchedTables }));\n      } catch (e) {\n        setError(t('errors.failed_load_tables') || 'Failed to load tables');\n        setTables([]);\n      } finally {\n        setLoadingTables(false);\n      }\n    }\n    fetchTables();\n  }, [selectedRoom]);\n\n  // Clear cache when modal closes\n  useEffect(() => {\n    if (!selectedRoom) {\n      setTablesCache({});\n    }\n  }, [onClose]);\n\n  return (\n    <Dialog open onOpenChange={onClose}>\n      <DialogContent className=\"bg-white rounded-lg w-full h-5/6 max-w-2xl mx-auto p-0 overflow-y-auto\">\n        <div className=\"p-4 border-b border-gray-200 flex justify-between items-center\">\n          <h2 className=\"text-lg font-semibold text-gray-900\">{t('common.select_table_title')}</h2>\n          <Button onClick={onClose} variant=\"ghost\" size=\"icon\">\n            <X className=\"w-5 h-5\" />\n          </Button>\n        </div>\n        <div className=\"p-4\">\n          {/* Room Selection */}\n          {loadingRooms ? (\n            <div className=\"mb-6\">\n              <Spinner message={t('common.loading_rooms')} />\n            </div>\n          ) : error ? (\n            <div className=\"mb-6 flex flex-col items-center justify-center gap-2 text-red-500\">\n              <AlertTriangle className=\"w-8 h-8 mb-1\" />\n              <span>{error}</span>\n            </div>\n          ) : rooms.length === 0 ? (\n            <div className=\"mb-6 flex flex-col items-center justify-center gap-2 text-gray-400\">\n              <Square className=\"w-8 h-8 mb-1\" />\n              <span>{t('common.no_rooms_found')}</span>\n            </div>\n          ) : (\n            <div className=\"flex gap-2 mb-6\">\n              {rooms.map(room => (\n                <Button\n                  key={room.name}\n                  onClick={() => setSelectedRoom(room.name)}\n                  variant=\"tab\"\n                  data-selected={selectedRoom === room.name}\n                  className=\"h-fit\"\n                >\n                  {room.name}\n                </Button>\n              ))}\n            </div>\n          )}\n\n          {/* Table Grid */}\n          {loadingTables ? (\n            <div className=\"\">\n              <Spinner message={t('common.loading_tables')} />\n            </div>\n          ) : error ? (\n            <div className=\"flex flex-col items-center justify-center gap-2 text-red-500 mt-8\">\n              <AlertTriangle className=\"w-8 h-8 mb-1\" />\n              <span>{error}</span>\n            </div>\n          ) : tables.length === 0 ? (\n            <div className=\"flex flex-col items-center justify-center gap-2 text-gray-400 mt-8\">\n              <Square className=\"w-8 h-8 mb-1\" />\n              <span>{t('common.no_tables_found')}</span>\n            </div>\n          ) : (\n            <div className=\"grid grid-cols-3 gap-6\">\n              {tables.map(table => (\n                <Button\n                  key={table.name}\n                  onClick={() => {\n                    setSelectedTable(table.name, selectedRoom);\n                    onClose();\n                  }}\n                  variant=\"outline\"\n                  className={cn(\n                    'h-fit p-4 rounded-lg border-2 flex flex-col items-center gap-2 transition-colors relative',\n                    selectedTable === table.name\n                      ? 'border-primary-600 bg-primary-50'\n                      : table.occupied === 1\n                      ? 'border-amber-500 bg-amber-50 hover:border-amber-600 hover:bg-amber-100'\n                      : 'border-gray-200 hover:border-primary-300 hover:bg-gray-50',\n                    'focus-visible:ring-2 focus-visible:ring-primary-600',\n                  )}\n                >\n                  <TableShapeIcon\n                    shape={table.table_shape}\n                    className={cn(\n                      'w-8 h-8',\n                      table.occupied === 1 ? 'text-amber-500' : 'text-gray-500'\n                    )}\n                  />\n                  <div className=\"text-center\">\n                    <div className=\"font-medium\">{table.name}</div>\n                    <div className=\"mt-2 h-4\"> {/* Height placeholder that matches Badge height */}\n                      {table.occupied === 1 && (\n                        <Badge variant=\"secondary\" className=\"text-xs bg-amber-100 text-amber-700 hover:bg-amber-100\">\n                          {t('tables.occupied')}\n                        </Badge>\n                      )}\n                    </div>\n                  </div>\n                </Button>\n              ))}\n            </div>\n          )}\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nexport default TableSelectionDialog; "
  },
  {
    "path": "pos/src/components/TableShapeIcon.tsx",
    "content": "import { Circle, RectangleHorizontal, Square } from 'lucide-react';\nimport type { Table } from '../lib/table-api';\n\ninterface TableShapeIconProps {\n  shape?: Table['table_shape'];\n  className?: string;\n}\n\nexport const TableShapeIcon = ({ shape = 'Rectangle', className }: TableShapeIconProps) => {\n  switch (shape) {\n    case 'Circle':\n      return <Circle className={className} />;\n    case 'Square':\n      return <Square className={className} />;\n    case 'Rectangle':\n    default:\n      return <RectangleHorizontal className={className} />;\n  }\n};\n\n"
  },
  {
    "path": "pos/src/components/ui/README.md",
    "content": "# UI Components\n\nThis directory contains reusable UI components built with class-variance-authority (CVA) for consistent styling and variants.\n\n## Components\n\n### Button\nA versatile button component with multiple variants and sizes.\n\n```tsx\nimport { Button } from \"@/components/ui\"\n\n// Variants: default, destructive, outline, secondary, ghost, link, success, warning, danger\n// Sizes: default, sm, lg, icon, xs\n\n<Button variant=\"default\" size=\"default\">\n  Click me\n</Button>\n\n<Button variant=\"success\" size=\"sm\">\n  Success\n</Button>\n\n<Button variant=\"danger\" size=\"lg\">\n  Delete\n</Button>\n```\n\n### Select\nA styled select component with error states.\n\n```tsx\nimport { Select } from \"@/components/ui\"\n\n// Variants: default, error, success\n// Sizes: default, sm, lg\n\n<Select variant=\"default\" size=\"default\">\n  <option value=\"\">Select an option</option>\n  <option value=\"option1\">Option 1</option>\n  <option value=\"option2\">Option 2</option>\n</Select>\n\n<Select variant=\"error\" size=\"sm\">\n  <option value=\"\">Select an option</option>\n</Select>\n```\n\n### Input\nA styled input component with various states.\n\n```tsx\nimport { Input } from \"@/components/ui\"\n\n// Variants: default, error, success, search\n// Sizes: default, sm, lg\n\n<Input \n  type=\"text\" \n  placeholder=\"Enter text...\" \n  variant=\"default\" \n  size=\"default\" \n/>\n\n<Input \n  type=\"email\" \n  placeholder=\"Email\" \n  variant=\"error\" \n  size=\"sm\" \n/>\n```\n\n### Badge\nA badge component for status indicators and labels.\n\n```tsx\nimport { Badge } from \"@/components/ui\"\n\n// Variants: default, secondary, destructive, outline, success, warning, danger, info, pending, completed, cancelled\n// Sizes: default, sm, lg\n\n<Badge variant=\"success\">Completed</Badge>\n<Badge variant=\"warning\">Pending</Badge>\n<Badge variant=\"danger\">Error</Badge>\n<Badge variant=\"info\" size=\"sm\">Info</Badge>\n```\n\n### Card\nA card component with header, content, and footer sections.\n\n```tsx\nimport { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from \"@/components/ui\"\n\n// Variants: default, elevated, outlined, ghost\n// Padding: none, sm, default, lg, xl\n\n<Card variant=\"default\" padding=\"default\">\n  <CardHeader>\n    <CardTitle>Card Title</CardTitle>\n    <CardDescription>Card description</CardDescription>\n  </CardHeader>\n  <CardContent>\n    <p>Card content goes here</p>\n  </CardContent>\n  <CardFooter>\n    <Button>Action</Button>\n  </CardFooter>\n</Card>\n```\n\n### Dialog\nA modal dialog component with overlay and content sections.\n\n```tsx\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from \"@/components/ui\"\n\n// Variants: default, fullscreen\n// Sizes: default, sm, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl, 7xl\n\n<Dialog open={isOpen} onOpenChange={setIsOpen}>\n  <DialogContent variant=\"default\" size=\"lg\">\n    <DialogHeader>\n      <DialogTitle>Dialog Title</DialogTitle>\n      <DialogDescription>Dialog description</DialogDescription>\n    </DialogHeader>\n    <div>Dialog content</div>\n    <DialogFooter>\n      <Button variant=\"outline\" onClick={() => setIsOpen(false)}>\n        Cancel\n      </Button>\n      <Button onClick={handleConfirm}>\n        Confirm\n      </Button>\n    </DialogFooter>\n  </DialogContent>\n</Dialog>\n```\n\n## Usage\n\nImport components from the UI index:\n\n```tsx\nimport { Button, Input, Select, Badge, Card, Dialog } from \"@/components/ui\"\n```\n\nOr import individual components:\n\n```tsx\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\n```\n\n## Styling\n\nAll components use Tailwind CSS classes and CSS custom properties for theming. The components are designed to work with the design system defined in `tailwind.config.js` and `src/index.css`.\n\n## Variants\n\nComponents use class-variance-authority (CVA) for consistent variant management. Each component exports its variants function for advanced customization:\n\n```tsx\nimport { buttonVariants } from \"@/components/ui/button\"\n\nconst customButtonClass = buttonVariants({ \n  variant: \"outline\", \n  size: \"lg\",\n  className: \"custom-class\" \n})\n``` "
  },
  {
    "path": "pos/src/components/ui/badge.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\n\nconst badgeVariants = cva(\n  \"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border-transparent bg-primary text-primary-foreground hover:bg-primary/80\",\n        secondary:\n          \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        destructive:\n          \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",\n        outline: \"text-foreground\",\n        success: \"border-transparent bg-green-100 text-green-800\",\n        warning: \"border-transparent bg-orange-100 text-orange-800\",\n        danger: \"border-transparent bg-red-100 text-red-800\",\n        info: \"border-transparent bg-blue-100 text-blue-800\",\n        pending: \"border-transparent bg-yellow-100 text-yellow-800\",\n        completed: \"border-transparent bg-green-100 text-green-800\",\n        cancelled: \"border-transparent bg-gray-100 text-gray-800\",\n      },\n      size: {\n        default: \"px-2.5 py-0.5 text-xs\",\n        sm: \"px-2 py-0.5 text-xs\",\n        lg: \"px-3 py-1 text-sm\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface BadgeProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, size, ...props }: BadgeProps) {\n  return (\n    <div className={cn(badgeVariants({ variant, size }), className)} {...props} />\n  )\n}\n\nexport { Badge, badgeVariants } "
  },
  {
    "path": "pos/src/components/ui/button.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-white hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-white hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"underline-offset-4 hover:underline text-primary\",\n        tab: \"bg-gray-100 text-gray-700 rounded-lg px-4 py-2 font-medium border-0 transition-colors data-[selected=true]:bg-primary-50 data-[selected=true]:text-primary-700 hover:bg-gray-200\",\n        success: \"bg-green-600 text-white hover:bg-green-700\",\n        warning: \"bg-orange-600 text-white hover:bg-orange-700\",\n        danger: \"bg-red-600 text-white hover:bg-red-700\",\n      },\n      size: {\n        default: \"h-10 py-2 px-4\",\n        sm: \"h-9 px-3 rounded-md\",\n        lg: \"h-11 px-8 rounded-md\",\n        icon: \"h-10 w-10\",\n        xs: \"h-7 px-2 text-xs\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    return (\n      <button\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants } "
  },
  {
    "path": "pos/src/components/ui/card.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\n\nconst cardVariants = cva(\n  \"rounded-lg border bg-card text-card-foreground shadow-sm\",\n  {\n    variants: {\n      variant: {\n        default: \"border-gray-200 bg-white\",\n        elevated: \"border-gray-200 bg-white shadow-md\",\n        outlined: \"border-gray-300 bg-white\",\n        ghost: \"border-transparent bg-transparent\",\n      },\n      padding: {\n        none: \"\",\n        sm: \"p-3\",\n        default: \"p-4\",\n        lg: \"p-6\",\n        xl: \"p-8\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      padding: \"default\",\n    },\n  }\n)\n\nexport interface CardProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof cardVariants> {}\n\nconst Card = React.forwardRef<HTMLDivElement, CardProps>(\n  ({ className, variant, padding, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(cardVariants({ variant, padding, className }))}\n      {...props}\n    />\n  )\n)\nCard.displayName = \"Card\"\n\nconst CardHeader = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n    {...props}\n  />\n))\nCardHeader.displayName = \"CardHeader\"\n\nconst CardTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h3\n    ref={ref}\n    className={cn(\n      \"text-2xl font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n))\nCardTitle.displayName = \"CardTitle\"\n\nconst CardDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <p\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nCardDescription.displayName = \"CardDescription\"\n\nconst CardContent = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n))\nCardContent.displayName = \"CardContent\"\n\nconst CardFooter = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex items-center p-6 pt-0\", className)}\n    {...props}\n  />\n))\nCardFooter.displayName = \"CardFooter\"\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent, cardVariants } "
  },
  {
    "path": "pos/src/components/ui/dialog.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\nimport { X } from \"lucide-react\"\n\nconst dialogVariants = cva(\n  \"fixed inset-0 z-50 flex items-center justify-center\",\n  {\n    variants: {\n      variant: {\n        default: \"\",\n        fullscreen: \"p-0\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst overlayVariants = cva(\n  \"fixed inset-0 bg-black/50 backdrop-blur-sm transition-opacity\",\n  {\n    variants: {\n      variant: {\n        default: \"\",\n        fullscreen: \"bg-black/80\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst contentVariants = cva(\n  \"relative bg-white rounded-lg shadow-lg max-h-[90vh] overflow-hidden\",\n  {\n    variants: {\n      variant: {\n        default: \"w-full max-w-md mx-4\",\n        fullscreen: \"w-full h-full max-w-none mx-0 rounded-none\",\n        large: \"w-full max-w-2xl mx-4\",\n        xlarge: \"w-full max-w-4xl mx-4\",\n      },\n      size: {\n        default: \"\",\n        sm: \"max-w-sm\",\n        lg: \"max-w-lg\",\n        xl: \"max-w-xl\",\n        \"2xl\": \"max-w-2xl\",\n        \"3xl\": \"max-w-3xl\",\n        \"4xl\": \"max-w-4xl\",\n        \"5xl\": \"max-w-5xl\",\n        \"6xl\": \"max-w-6xl\",\n        \"7xl\": \"max-w-7xl\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface DialogProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof dialogVariants> {\n  open?: boolean\n  onOpenChange?: (open: boolean) => void\n}\n\nconst Dialog = React.forwardRef<HTMLDivElement, DialogProps>(\n  ({ className, variant, open, onOpenChange, children, ...props }, ref) => {\n    if (!open) return null\n\n    return (\n      <div\n        ref={ref}\n        className={cn(dialogVariants({ variant, className }))}\n        {...props}\n      >\n        <div\n          className={cn(overlayVariants({ variant }))}\n          onClick={() => onOpenChange?.(false)}\n        />\n        {children}\n      </div>\n    )\n  }\n)\nDialog.displayName = \"Dialog\"\n\nexport interface DialogContentProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof contentVariants> {\n  onClose?: () => void\n  showCloseButton?: boolean\n}\n\nconst DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(\n  ({ className, variant, size, onClose, showCloseButton = true, children, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(contentVariants({ variant, size, className }))}\n      onClick={(e) => e.stopPropagation()}\n      {...props}\n    >\n      {showCloseButton && onClose && (\n        <button\n          onClick={onClose}\n          className=\"absolute top-4 right-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\"\n        >\n          <X className=\"h-4 w-4\" />\n          <span className=\"sr-only\">Close</span>\n        </button>\n      )}\n      {children}\n    </div>\n  )\n)\nDialogContent.displayName = \"DialogContent\"\n\nconst DialogHeader = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col space-y-1.5 text-center sm:text-left p-6\", className)}\n    {...props}\n  />\n))\nDialogHeader.displayName = \"DialogHeader\"\n\nconst DialogFooter = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 p-6 pt-0\", className)}\n    {...props}\n  />\n))\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h2\n    ref={ref}\n    className={cn(\"text-lg font-semibold leading-none tracking-tight\", className)}\n    {...props}\n  />\n))\nDialogTitle.displayName = \"DialogTitle\"\n\nconst DialogDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <p\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nDialogDescription.displayName = \"DialogDescription\"\n\nexport { Dialog, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, dialogVariants, contentVariants } "
  },
  {
    "path": "pos/src/components/ui/example.tsx",
    "content": "import React, { useState } from 'react';\nimport { Button, Input, Select, Badge, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from './index';\n\nexport const UIExample = () => {\n  const [dialogOpen, setDialogOpen] = useState(false);\n  const [inputValue, setInputValue] = useState('');\n  const [selectValue, setSelectValue] = useState('');\n\n  return (\n    <div className=\"p-8 space-y-8\">\n      <h1 className=\"text-3xl font-bold\">UI Components Example</h1>\n      \n      {/* Button Examples */}\n      <Card>\n        <CardHeader>\n          <CardTitle>Buttons</CardTitle>\n          <CardDescription>Different button variants and sizes</CardDescription>\n        </CardHeader>\n        <CardContent className=\"space-y-4\">\n          <div className=\"flex flex-wrap gap-2\">\n            <Button variant=\"default\">Default</Button>\n            <Button variant=\"secondary\">Secondary</Button>\n            <Button variant=\"outline\">Outline</Button>\n            <Button variant=\"ghost\">Ghost</Button>\n            <Button variant=\"link\">Link</Button>\n          </div>\n          <div className=\"flex flex-wrap gap-2\">\n            <Button variant=\"success\">Success</Button>\n            <Button variant=\"warning\">Warning</Button>\n            <Button variant=\"danger\">Danger</Button>\n          </div>\n          <div className=\"flex flex-wrap gap-2\">\n            <Button size=\"xs\">Extra Small</Button>\n            <Button size=\"sm\">Small</Button>\n            <Button size=\"default\">Default</Button>\n            <Button size=\"lg\">Large</Button>\n            <Button size=\"icon\">🚀</Button>\n          </div>\n        </CardContent>\n      </Card>\n\n      {/* Input Examples */}\n      <Card>\n        <CardHeader>\n          <CardTitle>Inputs</CardTitle>\n          <CardDescription>Different input variants and states</CardDescription>\n        </CardHeader>\n        <CardContent className=\"space-y-4\">\n          <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">Default Input</label>\n              <Input \n                placeholder=\"Enter text...\" \n                value={inputValue}\n                onChange={(e) => setInputValue(e.target.value)}\n              />\n            </div>\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">Search Input</label>\n              <Input \n                placeholder=\"Search...\" \n                variant=\"search\"\n              />\n            </div>\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">Error Input</label>\n              <Input \n                placeholder=\"Error state\" \n                variant=\"error\"\n              />\n            </div>\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">Success Input</label>\n              <Input \n                placeholder=\"Success state\" \n                variant=\"success\"\n              />\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n\n      {/* Select Examples */}\n      <Card>\n        <CardHeader>\n          <CardTitle>Select</CardTitle>\n          <CardDescription>Select component with different states</CardDescription>\n        </CardHeader>\n        <CardContent className=\"space-y-4\">\n          <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">Default Select</label>\n              <Select \n                value={selectValue}\n                onChange={(e) => setSelectValue(e.target.value)}\n              >\n                <option value=\"\">Select an option</option>\n                <option value=\"option1\">Option 1</option>\n                <option value=\"option2\">Option 2</option>\n                <option value=\"option3\">Option 3</option>\n              </Select>\n            </div>\n            <div>\n              <label className=\"block text-sm font-medium mb-2\">Error Select</label>\n              <Select variant=\"error\">\n                <option value=\"\">Select an option</option>\n                <option value=\"option1\">Option 1</option>\n                <option value=\"option2\">Option 2</option>\n              </Select>\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n\n      {/* Badge Examples */}\n      <Card>\n        <CardHeader>\n          <CardTitle>Badges</CardTitle>\n          <CardDescription>Status indicators and labels</CardDescription>\n        </CardHeader>\n        <CardContent>\n          <div className=\"flex flex-wrap gap-2\">\n            <Badge variant=\"default\">Default</Badge>\n            <Badge variant=\"secondary\">Secondary</Badge>\n            <Badge variant=\"outline\">Outline</Badge>\n            <Badge variant=\"success\">Success</Badge>\n            <Badge variant=\"warning\">Warning</Badge>\n            <Badge variant=\"danger\">Danger</Badge>\n            <Badge variant=\"info\">Info</Badge>\n            <Badge variant=\"pending\">Pending</Badge>\n            <Badge variant=\"completed\">Completed</Badge>\n            <Badge variant=\"cancelled\">Cancelled</Badge>\n          </div>\n          <div className=\"flex flex-wrap gap-2 mt-4\">\n            <Badge size=\"sm\">Small</Badge>\n            <Badge size=\"default\">Default</Badge>\n            <Badge size=\"lg\">Large</Badge>\n          </div>\n        </CardContent>\n      </Card>\n\n      {/* Dialog Example */}\n      <Card>\n        <CardHeader>\n          <CardTitle>Dialog</CardTitle>\n          <CardDescription>Modal dialog component</CardDescription>\n        </CardHeader>\n        <CardContent>\n          <Button onClick={() => setDialogOpen(true)}>\n            Open Dialog\n          </Button>\n        </CardContent>\n      </Card>\n\n      {/* Dialog */}\n      <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>\n        <DialogContent size=\"lg\">\n          <DialogHeader>\n            <DialogTitle>Example Dialog</DialogTitle>\n            <DialogDescription>\n              This is an example of the dialog component with all its features.\n            </DialogDescription>\n          </DialogHeader>\n          <div className=\"py-4\">\n            <p>This dialog demonstrates the usage of the Dialog component with header, content, and footer sections.</p>\n          </div>\n          <DialogFooter>\n            <Button variant=\"outline\" onClick={() => setDialogOpen(false)}>\n              Cancel\n            </Button>\n            <Button onClick={() => setDialogOpen(false)}>\n              Confirm\n            </Button>\n          </DialogFooter>\n        </DialogContent>\n      </Dialog>\n    </div>\n  );\n};\n\nexport default UIExample; "
  },
  {
    "path": "pos/src/components/ui/index.ts",
    "content": "export * from './button';\nexport * from './dialog';\nexport * from './input';\nexport * from './select';\nexport * from './badge';\nexport * from './spinner';\nexport * from './card';"
  },
  {
    "path": "pos/src/components/ui/input.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\n\nconst inputVariants = cva(\n  \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"border-gray-200 focus:border-blue-500 focus:ring-blue-200\",\n        error: \"border-red-300 focus:border-red-500 focus:ring-red-200\",\n        success: \"border-green-300 focus:border-green-500 focus:ring-green-200\",\n        search: \"border-gray-200 bg-gray-50 focus:border-blue-500 focus:ring-blue-200\",\n      },\n      size: {\n        default: \"h-10 px-3 py-2\",\n        sm: \"h-8 px-2 py-1 text-xs\",\n        lg: \"h-12 px-4 py-3\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface InputProps\n  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,\n    VariantProps<typeof inputVariants> {\n  error?: boolean\n}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, variant, size, error, type, ...props }, ref) => {\n    const inputVariant = error ? \"error\" : variant\n    \n    return (\n      <input\n        type={type}\n        className={cn(inputVariants({ variant: inputVariant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nInput.displayName = \"Input\"\n\nexport { Input, inputVariants } "
  },
  {
    "path": "pos/src/components/ui/loader.tsx",
    "content": "import React from 'react';\nimport { t } from '../../i18n';\n\nconst Loader: React.FC<{ message?: string }> = ({ message }) => {\n  const displayMessage = message ?? t('common.loading');\n  return (\n    <div className=\"flex flex-col items-center justify-center h-full w-full py-12\">\n      <svg className=\"animate-spin h-8 w-8 text-blue-600 mb-3\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n        <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n        <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\" />\n      </svg>\n      <span className=\"text-gray-600 text-sm\" role=\"status\" aria-live=\"polite\">{displayMessage}</span>\n    </div>\n  );\n};\n\nexport default Loader; "
  },
  {
    "path": "pos/src/components/ui/select.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\nimport { ChevronDown } from \"lucide-react\"\nimport * as RadixSelect from \"@radix-ui/react-select\"\n\nconst selectVariants = cva(\n  \"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-4 py-2 text-sm font-normal ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-colors\",\n  {\n    variants: {\n      variant: {\n        default: \"border-gray-200 focus:border-blue-500 focus:ring-blue-200\",\n        error: \"border-red-300 focus:border-red-500 focus:ring-red-200\",\n        success: \"border-green-300 focus:border-green-500 focus:ring-green-200\",\n      },\n      size: {\n        default: \"h-10 px-3 py-2\",\n        sm: \"h-8 px-2 py-1 text-xs\",\n        lg: \"h-12 px-4 py-3\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface SelectProps extends Omit<React.ComponentPropsWithoutRef<typeof RadixSelect.Root>, 'size'>, VariantProps<typeof selectVariants> {\n  error?: boolean\n  children: React.ReactNode\n  placeholder?: string\n  className?: string\n}\n\nconst Select = React.forwardRef<HTMLButtonElement, SelectProps>(\n  ({ className, variant, size, error, children, placeholder = \"Select an option\", ...props }, ref) => {\n    const selectVariant = error ? \"error\" : variant\n    return (\n      <RadixSelect.Root {...props}>\n        <RadixSelect.Trigger\n          ref={ref}\n          className={cn(selectVariants({ variant: selectVariant, size, className }))}\n        >\n          <RadixSelect.Value placeholder={placeholder} className=\"placeholder:text-muted-foreground\" />\n          <RadixSelect.Icon asChild>\n            <ChevronDown className=\"ml-2 w-4 h-4 text-gray-400\" />\n          </RadixSelect.Icon>\n        </RadixSelect.Trigger>\n        <RadixSelect.Portal>\n          <RadixSelect.Content\n            className=\"z-50 w-[var(--radix-select-trigger-width)] bg-white border border-gray-200 rounded-lg shadow-lg mt-2 max-h-80 overflow-y-auto px-0 py-2 focus:outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-500\"\n            position=\"popper\"\n            sideOffset={4}\n          >\n            <RadixSelect.Viewport>\n              {children}\n            </RadixSelect.Viewport>\n          </RadixSelect.Content>\n        </RadixSelect.Portal>\n      </RadixSelect.Root>\n    )\n  }\n)\nSelect.displayName = \"Select\"\n\n// Export a properly styled SelectItem component\nconst SelectItem = React.forwardRef<\n  React.ElementRef<typeof RadixSelect.Item>,\n  React.ComponentPropsWithoutRef<typeof RadixSelect.Item>\n>(({ className, children, ...props }, ref) => (\n  <RadixSelect.Item\n    ref={ref}\n    className={cn(\n      \"px-4 py-2 text-sm text-gray-800 rounded-md cursor-pointer select-none transition-colors\",\n      \"hover:bg-gray-50 focus:bg-gray-50\",\n      \"data-[state=checked]:bg-primary-50 data-[state=checked]:text-primary-700 font-normal\",\n      className\n    )}\n    {...props}\n  >\n    <RadixSelect.ItemText>{children}</RadixSelect.ItemText>\n  </RadixSelect.Item>\n))\nSelectItem.displayName = \"SelectItem\"\n\nexport { Select, SelectItem, selectVariants, RadixSelect } "
  },
  {
    "path": "pos/src/components/ui/spinner.tsx",
    "content": "import { cn } from '../../lib/utils';\nimport { t } from '../../i18n';\n\ninterface SpinnerProps {\n  className?: string;\n  message?: string;\n  hideMessage?: boolean;\n}\n\nexport function Spinner({ className, message, hideMessage = false}: SpinnerProps) {\n  const displayMessage = message ?? t('common.loading');\n  return (\n    <div className=\"flex items-center justify-center min-h-[inherit]\">\n      <div className=\"text-center\">\n        <div className={cn(\n          \"animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-600 mx-auto\",\n          className\n        )} />\n        {!hideMessage && displayMessage && <p className=\"mt-4 text-gray-600\">{displayMessage}</p>}\n      </div>\n    </div>\n  );\n}"
  },
  {
    "path": "pos/src/components/ui/textarea.tsx",
    "content": "import * as React from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface TextareaProps\n  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n  ({ className, ...props }, ref) => {\n    return (\n      <textarea\n        className={cn(\n          \"flex min-h-[80px] w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    );\n  }\n);\n\nTextarea.displayName = \"Textarea\";\n\nexport { Textarea }; "
  },
  {
    "path": "pos/src/components/ui/toast.css",
    "content": "/* Override default toast styles for custom design */\n.Toastify__toast {\n  border-radius: 8px !important;\n  font-family: inherit !important;\n  padding: 16px !important;\n  min-height: auto !important;\n}\n\n.Toastify__toast-icon {\n  margin-right: 12px !important;\n  width: 20px !important;\n}\n\n/* Success Toast */\n.toast-success {\n  background-color: #ecfdf5 !important;\n  color: #065f46 !important;\n}\n\n.toast-success .Toastify__toast-icon svg {\n  color: #059669 !important;\n}\n\n.toast-success .Toastify__progress-bar {\n  background-color: #059669 !important;\n}\n\n.toast-success .Toastify__close-button {\n  color: #065f46 !important;\n}\n\n/* Error Toast */\n.toast-error {\n  background-color: #fef2f2 !important;\n  color: #991b1b !important;\n}\n\n.toast-error .Toastify__toast-icon svg {\n  color: #dc2626 !important;\n}\n\n.toast-error .Toastify__progress-bar {\n  background-color: #dc2626 !important;\n}\n\n.toast-error .Toastify__close-button {\n  color: #991b1b !important;\n}\n\n/* Info Toast */\n.toast-info {\n  background-color: #eff6ff !important;\n  color: #1e40af !important;\n}\n\n.toast-info .Toastify__toast-icon svg {\n  color: #3b82f6 !important;\n}\n\n.toast-info .Toastify__progress-bar {\n  background-color: #3b82f6 !important;\n}\n\n.toast-info .Toastify__close-button {\n  color: #1e40af !important;\n}\n\n/* Common styles for all toasts */\n.Toastify__toast-body {\n  font-size: 0.875rem !important;\n  line-height: 1.25rem !important;\n  font-weight: 500 !important;\n}\n\n.Toastify__close-button {\n  opacity: 0.8 !important;\n  padding: 0 !important;\n  margin-left: 12px !important;\n}\n\n.Toastify__close-button:hover {\n  opacity: 1 !important;\n} "
  },
  {
    "path": "pos/src/components/ui/toast.tsx",
    "content": "import { toast, ToastContainer } from 'react-toastify';\nimport { CheckCircle, XCircle, Info } from 'lucide-react';\nimport 'react-toastify/dist/ReactToastify.css';\n\n// Custom CSS for toast styling\nimport './toast.css';\n\nconst toastIcons = {\n  success: <CheckCircle className=\"w-5 h-5\" />,\n  error: <XCircle className=\"w-5 h-5\" />,\n  info: <Info className=\"w-5 h-5\" />,\n};\n\nexport const showToast = {\n  success: (message: string) => {\n    toast.success(message, {\n      position: 'top-right',\n      autoClose: 2000,\n      hideProgressBar: false,\n      closeOnClick: true,\n      pauseOnHover: true,\n      draggable: true,\n      progress: undefined,\n      theme: 'colored',\n      icon: toastIcons.success,\n      className: 'toast-success',\n    });\n  },\n  error: (message: string) => {\n    toast.error(message, {\n      position: 'top-right',\n      autoClose: 2000,\n      hideProgressBar: false,\n      closeOnClick: true,\n      pauseOnHover: true,\n      draggable: true,\n      progress: undefined,\n      theme: 'colored',\n      icon: toastIcons.error,\n      className: 'toast-error',\n    });\n  },\n  info: (message: string) => {\n    toast.info(message, {\n      position: 'top-right',\n      autoClose: 2000,\n      hideProgressBar: false,\n      closeOnClick: true,\n      pauseOnHover: true,\n      draggable: true,\n      progress: undefined,\n      theme: 'colored',\n      icon: toastIcons.info,\n      className: 'toast-info',\n    });\n  },\n};\n\nexport const ToastProvider = () => {\n  return (\n    <ToastContainer\n      position=\"top-right\"\n      autoClose={2000}\n      hideProgressBar={false}\n      newestOnTop\n      closeOnClick\n      rtl={false}\n      pauseOnFocusLoss\n      draggable\n      pauseOnHover\n      theme=\"colored\"\n    />\n  );\n}; "
  },
  {
    "path": "pos/src/data/doctypes.ts",
    "content": "export const DOCTYPES={\n    \"POS_PROFILE\": \"POS Profile\",\n    \"URY_MENU_COURSE\": \"URY Menu Course\",\n    \"URY_ROOM\": \"URY Room\",\n    \"URY_TABLE\": \"URY Table\",\n    \"CUSTOMER\": \"Customer\",\n    \"CUSTOMER_GROUP\": \"Customer Group\",\n    \"CUSTOMER_TERRITORY\": \"Territory\",\n    \"CURRENCY\": \"Currency\",\n}"
  },
  {
    "path": "pos/src/data/menu-data.ts",
    "content": "// Sample menu data\nexport const menuData = [\n  // Breakfast Items\n  {\n    id: 'b1',\n    name: 'Plain Dosa',\n    price: 45,\n    category: 'Breakfast',\n    image: 'https://images.unsplash.com/photo-1589301760014-d929f3979dbc?w=500',\n    description: 'Crispy South Indian crepe served with coconut chutney and sambar',\n    popular: true,\n    variants: [\n      { id: 'b1-1', name: 'Single', price: 45 },\n      { id: 'b1-2', name: 'Double', price: 80 }\n    ],\n    addons: [\n      { id: '1', name: 'Extra Chutney', price: 10, category: 'sides' as const },\n      { id: '2', name: 'Extra Sambar', price: 15, category: 'sides' as const },\n      { id: '3', name: 'Filter Coffee', price: 25, category: 'drinks' as const }\n    ]\n  },\n  {\n    id: 'b2',\n    name: 'Masala Dosa',\n    price: 65,\n    category: 'Breakfast',\n    image: 'https://images.unsplash.com/photo-1630383249896-424e482df921?w=500',\n    description: 'Crispy dosa filled with spiced potato masala',\n    trending: true,\n    variants: [\n      { id: 'b2-1', name: 'Regular', price: 65 },\n      { id: 'b2-2', name: 'Rava Masala', price: 75 }\n    ],\n    addons: [\n      { id: '1', name: 'Extra Chutney', price: 10, category: 'sides' as const },\n      { id: '2', name: 'Extra Sambar', price: 15, category: 'sides' as const }\n    ]\n  },\n  {\n    id: 'b3',\n    name: 'Idli (2 pcs)',\n    price: 35,\n    category: 'Breakfast',\n    image: 'https://images.unsplash.com/photo-1589301760014-d929f3979dbc?w=500',\n    description: 'Soft steamed rice cakes served with chutney and sambar',\n    recommended: true,\n    variants: [\n      { id: 'b3-1', name: '2 pieces', price: 35 },\n      { id: 'b3-2', name: '4 pieces', price: 60 }\n    ],\n    addons: [\n      { id: '1', name: 'Extra Chutney', price: 10, category: 'sides' as const },\n      { id: '2', name: 'Extra Sambar', price: 15, category: 'sides' as const }\n    ]\n  },\n\n  // Lunch Items\n  {\n    id: 'l1',\n    name: 'Chicken Biryani',\n    price: 180,\n    category: 'Lunch',\n    image: 'https://images.unsplash.com/photo-1631452180519-c014fe946bc7?w=500',\n    description: 'Aromatic basmati rice cooked with tender chicken and spices',\n    popular: true,\n    variants: [\n      { id: 'l1-1', name: 'Half', price: 180 },\n      { id: 'l1-2', name: 'Full', price: 320 }\n    ],\n    addons: [\n      { id: '5', name: 'Raita', price: 25, category: 'sides' as const },\n      { id: '6', name: 'Pickle', price: 15, category: 'sides' as const },\n      { id: '7', name: 'Boiled Egg', price: 20, category: 'sides' as const }\n    ]\n  },\n  {\n    id: 'l2',\n    name: 'Mutton Biryani',\n    price: 220,\n    category: 'Lunch',\n    image: 'https://images.unsplash.com/photo-1642821373181-696a54913e93?w=500',\n    description: 'Premium basmati rice with tender mutton pieces and aromatic spices',\n    trending: true,\n    variants: [\n      { id: 'l2-1', name: 'Half', price: 220 },\n      { id: 'l2-2', name: 'Full', price: 380 }\n    ],\n    addons: [\n      { id: '5', name: 'Raita', price: 25, category: 'sides' as const },\n      { id: '6', name: 'Pickle', price: 15, category: 'sides' as const },\n      { id: '7', name: 'Boiled Egg', price: 20, category: 'sides' as const }\n    ]\n  },\n\n  // Burgers\n  {\n    id: 'br1',\n    name: 'Chicken Burger',\n    price: 120,\n    category: 'Burgers',\n    image: 'https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=500',\n    description: 'Juicy chicken patty with fresh vegetables and special sauce',\n    popular: true,\n    variants: [\n      { id: 'br1-1', name: 'Regular', price: 120 },\n      { id: 'br1-2', name: 'Combo with Fries', price: 180 }\n    ],\n    addons: [\n      { id: '8', name: 'Extra Cheese', price: 20, category: 'sides' as const },\n      { id: '9', name: 'Bacon', price: 30, category: 'sides' as const }\n    ]\n  },\n  {\n    id: 'br2',\n    name: 'Veg Burger',\n    price: 100,\n    category: 'Burgers',\n    image: 'https://images.unsplash.com/photo-1571091718767-18b5b1457add?w=500',\n    description: 'Fresh vegetable patty with lettuce, tomato, and cheese',\n    recommended: true,\n    variants: [\n      { id: 'br2-1', name: 'Regular', price: 100 },\n      { id: 'br2-2', name: 'Combo with Fries', price: 150 }\n    ],\n    addons: [\n      { id: '8', name: 'Extra Cheese', price: 20, category: 'sides' as const },\n      { id: '10', name: 'Extra Patty', price: 40, category: 'sides' as const }\n    ]\n  },\n\n  // Pizza\n  {\n    id: 'p1',\n    name: 'Margherita Pizza',\n    price: 200,\n    category: 'Pizza',\n    image: 'https://images.unsplash.com/photo-1604382355076-af4b0eb60143?w=500',\n    description: 'Classic pizza with tomato sauce, mozzarella, and basil',\n    trending: true,\n    variants: [\n      { id: 'p1-1', name: 'Small (8\")', price: 200 },\n      { id: 'p1-2', name: 'Medium (12\")', price: 300 },\n      { id: 'p1-3', name: 'Large (14\")', price: 400 }\n    ],\n    addons: [\n      { id: '11', name: 'Extra Cheese', price: 50, category: 'sides' as const },\n      { id: '12', name: 'Mushrooms', price: 40, category: 'sides' as const }\n    ]\n  },\n\n  // Salads\n  {\n    id: 's1',\n    name: 'Caesar Salad',\n    price: 80,\n    category: 'Salads',\n    image: 'https://images.unsplash.com/photo-1546793665-c74683f339c1?w=500',\n    description: 'Fresh romaine lettuce with Caesar dressing and croutons',\n    recommended: true,\n    variants: [\n      { id: 's1-1', name: 'Regular', price: 80 },\n      { id: 's1-2', name: 'With Chicken', price: 120 }\n    ],\n    addons: [\n      { id: '13', name: 'Extra Dressing', price: 15, category: 'sides' as const },\n      { id: '14', name: 'Parmesan Cheese', price: 20, category: 'sides' as const }\n    ]\n  },\n\n  // Sides\n  {\n    id: 'sd1',\n    name: 'French Fries',\n    price: 60,\n    category: 'Sides',\n    image: 'https://images.unsplash.com/photo-1573080496219-bb080dd4f877?w=500',\n    description: 'Crispy golden fries served with ketchup',\n    popular: true,\n    variants: [\n      { id: 'sd1-1', name: 'Regular', price: 60 },\n      { id: 'sd1-2', name: 'Large', price: 90 }\n    ],\n    addons: [\n      { id: '15', name: 'Cheese Sauce', price: 20, category: 'sides' as const },\n      { id: '16', name: 'Chilli Mayo', price: 15, category: 'sides' as const }\n    ]\n  }\n]; "
  },
  {
    "path": "pos/src/data/order-types.ts",
    "content": "import { Globe, Phone, ShoppingBag, Truck, Utensils } from \"lucide-react\";\n\nexport type OrderType = \"Dine In\" | \"Take Away\" | \"Delivery\" | \"Phone In\" | \"Aggregators\";\n\nexport type OrderTypes= {\n    label: string;\n    value: OrderType;\n    icon: React.ElementType;\n}\n\nexport const ORDER_TYPES: OrderTypes[] = [\n    {\n        label: \"Dine In\",\n        value: \"Dine In\",\n        icon: Utensils\n    },\n    {\n        label: \"Take Away\",\n        value: \"Take Away\",\n        icon: ShoppingBag\n    },\n    {\n        label: \"Delivery\",\n        value: \"Delivery\",\n        icon: Truck\n    },\n    {\n        label: \"Phone In\",\n        value: \"Phone In\",\n        icon: Phone\n    },\n    {\n        label: \"Aggregators\",\n        value: \"Aggregators\",\n        icon: Globe\n    }\n]\n\nexport const DINE_IN=\"Dine In\"\nexport const DEFAULT_ORDER_TYPE=\"Take Away\"\nexport const DEFAULT_PAYMENT_MODE=\"Cash\"\n\nexport type OrderStatusType = \"Draft\" | \"Unbilled\" | \"Recently Paid\" | \"Paid\" | \"Consolidated\" | \"Return\";\n\n// Base status types that are always available\nexport const BASE_ORDER_STATUS_TYPES = [\n    {\n        label: \"Draft\",\n        value: \"Draft\"\n    },\n    {\n        label: \"Unbilled\",\n        value: \"Unbilled\"\n    }\n];\n\n// Recently Paid status that appears when paid_limit > 0\nexport const RECENTLY_PAID_STATUS_TYPE = [\n    {\n        label: \"Recently Paid\",\n        value: \"Recently Paid\"\n    }\n];\n\n// Extended status types that are only available when view_all_status is enabled\nexport const EXTENDED_ORDER_STATUS_TYPES = [\n    {\n        label: \"Paid\",\n        value: \"Paid\"\n    },\n    {\n        label: \"Consolidated\",\n        value: \"Consolidated\"\n    },\n    {\n        label: \"Return\",\n        value: \"Return\"\n    }\n];\n\n// Function to get order status types based on POS profile settings\nexport const getOrderStatusTypes = (viewAllStatus?: number, paidLimit?: number) => {\n    let statusTypes = [...BASE_ORDER_STATUS_TYPES];\n    \n    // Add Recently Paid if paid_limit > 0\n    if (paidLimit && paidLimit > 0) {\n        statusTypes.push(...RECENTLY_PAID_STATUS_TYPE);\n    }\n    \n    // Add extended statuses if view_all_status is enabled\n    if (viewAllStatus === 1) {\n        statusTypes.push(...EXTENDED_ORDER_STATUS_TYPES);\n    }\n    \n    return statusTypes;\n};\n\n// Legacy export for backward compatibility\nexport const ORDER_STATUS_TYPES = BASE_ORDER_STATUS_TYPES;"
  },
  {
    "path": "pos/src/i18n/config.ts",
    "content": "export const DEFAULT_LANGUAGE = 'en';\n\nexport const SUPPORTED_LANGUAGES: Record<string, string> = {\n  en: 'English',\n  fr: 'Français',\n  ar: 'العربية',\n};\n"
  },
  {
    "path": "pos/src/i18n/index.ts",
    "content": "import { loadLocale } from './loader';\nimport { DEFAULT_LANGUAGE } from './config';\nimport { resolveLanguage } from './resolve-language';\n\ntype TranslationMap = Record<string, unknown>;\n\nlet activeLocale: TranslationMap = {};\nlet activeLanguage: string = DEFAULT_LANGUAGE;\n\n/**\n * Load and activate a locale. Call this once before rendering the app.\n */\nexport async function initI18n(lang?: string): Promise<void> {\n  const resolvedLang = lang ?? resolveLanguage();\n  activeLocale = await loadLocale(resolvedLang);\n  activeLanguage = resolvedLang;\n}\n\n/**\n * Get the text direction for the active locale.\n * Reads `_meta.direction` from the locale JSON; defaults to 'ltr'.\n */\nexport function getActiveDirection(): 'ltr' | 'rtl' {\n  const meta = (activeLocale as Record<string, unknown>)._meta;\n  if (meta && typeof meta === 'object' && (meta as Record<string, unknown>).direction === 'rtl') {\n    return 'rtl';\n  }\n  return 'ltr';\n}\n\n/**\n * Apply lang and dir to <html> after i18n is resolved.\n * Call this once in main.tsx before rendering the React tree.\n */\nexport function applyDocumentLocale(): void {\n  const root = document.documentElement;\n  root.lang = activeLanguage;\n  root.dir = getActiveDirection();\n}\n\n/**\n * Get the currently active language code.\n */\nexport function getActiveLanguage(): string {\n  return activeLanguage;\n}\n\n/**\n * Translate a dot-notation key, with optional interpolation.\n *\n * Example:\n *   t('errors.user_not_logged_in')           → \"User not logged in\"\n *   t('common.greeting', { name: 'Alice' })  → \"Hello, Alice\"\n */\nexport function t(key: string, params?: Record<string, string>): string {\n  const parts = key.split('.');\n  let value: unknown = activeLocale;\n\n  for (const part of parts) {\n    if (value && typeof value === 'object') {\n      value = (value as Record<string, unknown>)[part];\n    } else {\n      value = undefined;\n      break;\n    }\n  }\n\n  if (typeof value !== 'string') {\n    // Return the key itself as a fallback so missing translations are visible\n    return key;\n  }\n\n  if (!params) return value;\n\n  return value.replace(/\\{\\{(\\w+)\\}\\}/g, (_, k) => params[k] ?? `{{${k}}}`);\n}\n"
  },
  {
    "path": "pos/src/i18n/loader.ts",
    "content": "import { DEFAULT_LANGUAGE } from './config';\n\ntype TranslationMap = Record<string, unknown>;\n\nconst cache: Record<string, TranslationMap> = {};\n\nexport async function loadLocale(lang: string): Promise<TranslationMap> {\n  if (cache[lang]) return cache[lang];\n\n  try {\n    const module = await import(`./locales/${lang}.json`);\n    cache[lang] = module.default as TranslationMap;\n    return cache[lang];\n  } catch {\n    if (lang !== DEFAULT_LANGUAGE) {\n      return loadLocale(DEFAULT_LANGUAGE);\n    }\n    return {};\n  }\n}\n"
  },
  {
    "path": "pos/src/i18n/locales/ar.json",
    "content": "{\n    \"_meta\": {\n        \"language_code\": \"ar\",\n        \"translation_method\": \"machine_translated\",\n        \"human_verified\": false,\n        \"last_updated\": \"2026-03-30\",\n        \"direction\": \"rtl\"\n    },\n    \"common\": {\n        \"loading\": \"جاري التحميل...\",\n        \"cancel\": \"إلغاء\",\n        \"save\": \"حفظ\",\n        \"apply\": \"تطبيق\",\n        \"change\": \"تغيير\",\n        \"all\": \"الكل\",\n        \"no_options\": \"لا توجد خيارات\",\n        \"searching\": \"جاري البحث...\",\n        \"no_items_found\": \"لم يتم العثور على عناصر\",\n        \"try_adjusting_filters\": \"حاول تعديل خيارات التصفية أو البحث\",\n        \"loading_tables\": \"جاري تحميل الطاولات...\",\n        \"loading_menu_items\": \"جاري تحميل قائمة الطعام...\",\n        \"error_loading_menu_items\": \"فشل في تحميل قائمة الطعام\",\n        \"loading_rooms\": \"جاري تحميل القاعات...\",\n        \"no_rooms_found\": \"لم يتم العثور على قاعات\",\n        \"no_tables_found\": \"لم يتم العثور على طاولات\",\n        \"select_table_title\": \"اختر طاولة\",\n        \"checking_pos_status\": \"جاري التحقق من حالة نقطة البيع...\",\n        \"cancelling\": \"جاري الإلغاء...\",\n        \"confirm_cancel\": \"تأكيد الإلغاء\",\n        \"loading_ury_pos\": \"جاري تحميل URY POS...\",\n        \"please_wait_setup\": \"يرجى الانتظار بينما نقوم بضبط الأشياء...\"\n    },\n    \"header\": {\n        \"search_placeholder_default\": \"ابحث عن الطلبات، عناصر القائمة، أو العملاء...\",\n        \"search_placeholder_orders\": \"ابحث عن الطلبات\",\n        \"search_placeholder_menu\": \"ابحث في القائمة\",\n        \"switch_to_desk\": \"التبديل إلى المكتب\",\n        \"clear_cache\": \"مسح ذاكرة التخزين المؤقت\",\n        \"logout\": \"تسجيل الخروج\"\n    },\n    \"menu\": {\n        \"special_items\": \"أصناف مميزة\"\n    },\n    \"pos_sidebar\": {\n        \"categories\": \"فئات\",\n        \"all_items\": \"جميع الأصناف\"\n    },\n    \"cart\": {\n        \"empty_title\": \"عربة التسوق فارغة\",\n        \"empty_subtitle\": \"أضف عناصر للبدء في طلبك\",\n        \"click_to_add\": \"انقر على العناصر لإضافتها\",\n        \"double_click_hint\": \"انقر مرتين لخيارات التخصيص\",\n        \"clear_cart\": \"إفراغ العربة\",\n        \"total\": \"الإجمالي\",\n        \"add_new_order\": \"إضافة طلب جديد\",\n        \"update_order\": \"تحديث الطلب\",\n        \"updating_order\": \"جاري تحديث الطلب...\",\n        \"processing_order\": \"جاري معالجة الطلب...\",\n        \"loading_order\": \"جاري تحميل تفاصيل الطلب...\",\n        \"edit_item\": \"تعديل العنصر\",\n        \"add_comment\": \"إضافة تعليق\",\n        \"edit_comment\": \"تعديل التعليق\"\n    },\n    \"order\": {\n        \"select_to_view\": \"حدد طلبًا لعرض التفاصيل\",\n        \"items_title\": \"عناصر الطلب\",\n        \"taxes_charges\": \"الضرائب والرسوم\",\n        \"cancel_order\": \"إلغاء الطلب\",\n        \"enter_cancel_reason\": \"أدخل سبب الإلغاء\",\n        \"print\": \"طباعة\",\n        \"payment\": \"الدفع\"\n    },\n    \"payment\": {\n        \"title\": \"الدفع\",\n        \"apply_discount\": \"تطبيق الخصم\",\n        \"discount_placeholder\": \"أدخل نسبة %\",\n        \"payment_methods\": \"طرق الدفع\",\n        \"amount_placeholder\": \"المبلغ\",\n        \"total_entered\": \"الإجمالي المدخل\",\n        \"order_summary\": \"ملخص الطلب\",\n        \"subtotal\": \"المجموع الفرعي\",\n        \"discount\": \"الخصم\",\n        \"adjustment\": \"التسوية\",\n        \"total\": \"الإجمالي\",\n        \"pay_button\": \"دفع {{amount}}\",\n        \"processing\": \"جاري المعالجة...\"\n    },\n    \"pos\": {\n        \"not_opened_title\": \"نقطة البيع غير مفتوحة\",\n        \"not_closed_title\": \"وردية نقطة البيع السابقة غير مغلقة\",\n        \"not_opened_message\": \"يجب فتح وردية نقطة بيع جديدة للبدء.\",\n        \"not_closed_message\": \"يجب إغلاق وردية نقطة البيع السابقة قبل البدء بوردية جديدة. يرجى إغلاقها للمتابعة.\",\n        \"reload_page\": \"إعادة تحميل الصفحة\",\n        \"switch_to_desk\": \"التبديل إلى لوحة التحكم (Desk)\"\n    },\n    \"customer\": {\n        \"search_placeholder\": \"ابحث عن عميل...\",\n        \"type_to_search\": \"يرجى الكتابة للبحث...\",\n        \"no_customers_found\": \"لم يتم العثور على عملاء\",\n        \"add_new\": \"إضافة عميل جديد\",\n        \"add_with_name\": \"إضافة \\\"{{name}}\\\"...\",\n        \"failed_search\": \"فشل البحث عن عملاء\",\n        \"add_customer_title\": \"إضافة عميل جديد\",\n        \"name_label\": \"الاسم\",\n        \"name_required\": \"الاسم مطلوب\",\n        \"phone_label\": \"رقم الهاتف\",\n        \"phone_required\": \"رقم الهاتف مطلوب\",\n        \"customer_group_label\": \"مجموعة العملاء\",\n        \"territory_label\": \"المنطقة\",\n        \"select_group\": \"حدد المجموعة\",\n        \"select_territory\": \"حدد المنطقة\",\n        \"adding\": \"جاري إضافة العميل...\",\n        \"add_button\": \"إضافة العميل\",\n        \"failed_create\": \"فشل في إنشاء العميل. يرجى المحاولة مرة أخرى.\"\n    },\n    \"comment\": {\n        \"title\": \"تعليقات الطلب\",\n        \"label\": \"أضف تعليقات لهذا الطلب\",\n        \"placeholder\": \"أدخل أي تعليمات خاصة أو تعليقات...\",\n        \"save_button\": \"حفظ التعليق\"\n    },\n    \"errors\": {\n        \"pos_profile_not_found\": \"لم يتم العثور على ملف تعريف نقطة البيع\",\n        \"pos_profile_not_loaded\": \"لم يتم تحميل ملف تعريف نقطة البيع بعد\",\n        \"user_not_logged_in\": \"المستخدم غير مسجل الدخول\",\n        \"failed_load_rooms\": \"فشل في تحميل القاعات\",\n        \"failed_load_tables\": \"فشل في تحميل الطاولات\",\n        \"select_aggregator\": \"يرجى تحديد منصة التجميع (aggregator) قبل المتابعة\",\n        \"select_customer\": \"يرجى تحديد عميل قبل المتابعة\",\n        \"select_table\": \"يرجى تحديد طاولة للطلبات من نوع {{order_type}}\",\n        \"failed_logout\": \"فشل في تسجيل الخروج. يرجى المحاولة مرة أخرى.\",\n        \"failed_cancel_order\": \"فشل في إلغاء الطلب\",\n        \"failed_edit_order\": \"فشل في تعديل الطلب\",\n        \"failed_fetch_invoices\": \"فشل في جلب فواتير نقطة البيع\",\n        \"failed_fetch_order\": \"فشل في جلب تفاصيل الطلب\",\n        \"failed_print\": \"فشل في طباعة الطلب\",\n        \"failed_process_order\": \"فشل في معالجة الطلب\",\n        \"no_active_order\": \"لم يتم العثور على طلب نشط لهذه الطاولة\",\n        \"qz_host_not_set\": \"مضيف QZ غير محدد\",\n        \"print_failed\": \"فشلت الطباعة: {{reason}}\",\n        \"invalid_discount\": \"يرجى إدخال قيمة خصم صحيحة\",\n        \"discount_exceeds_max\": \"لا يمكن أن تتجاوز نسبة الخصم 100٪\",\n        \"please_print_first\": \"يرجى طباعة الفاتورة قبل إتمام عملية الدفع\",\n        \"enter_cancel_reason\": \"يرجى إدخال سبب للإلغاء.\",\n        \"dine_in_restricted\": \"الطلبات المحلية غير متاحة للدور الخاص بك\"\n    },\n    \"success\": {\n        \"order_cancelled\": \"تم إلغاء الطلب بنجاح\",\n        \"order_created\": \"تم إنشاء الطلب بنجاح\",\n        \"order_updated\": \"تم تحديث الطلب بنجاح\",\n        \"printed\": \"تمت الطباعة بنجاح\",\n        \"payment_successful\": \"تمت عملية الدفع بنجاح\",\n        \"order_moved_to_draft\": \"تم نقل الطلب إلى المسودات بعد الطباعة.\"\n    },\n    \"orders\": {\n        \"no_orders_found\": \"لم يتم العثور على طلبات\",\n        \"click_to_view\": \"انقر على أي بطاقة طلب لعرض تفاصيلها\",\n        \"status_title\": \"حالة الطلب\",\n        \"pagination\": {\n            \"previous\": \"السابق\",\n            \"next\": \"التالي\",\n            \"page\": \"صفحة {{number}}\"\n        }\n    },\n    \"tables\": {\n        \"layout_view\": \"عرض المخطط\",\n        \"grid_view\": \"عرض الشبكة\",\n        \"edit_layout\": \"تعديل المخطط\",\n        \"finish_editing\": \"إنهاء التعديل\",\n        \"layout\": \"مخطط\",\n        \"available\": \"متاح\",\n        \"occupied\": \"مشغول\",\n        \"room\": \"غرفة\",\n        \"seats\": \"مقاعد\",\n        \"tap_to_start\": \"انقر لبدء طلب محلي جديد\",\n        \"no_tables_found\": \"لم يتم العثور على طاولات لهذه الغرفة\",\n        \"edit_settings\": \"تعديل إعدادات الطاولة\",\n        \"table_info\": \"معلومات الطاولة\",\n        \"table_name\": \"اسم الطاولة\",\n        \"capacity\": \"السعة\",\n        \"shape\": \"الشكل\",\n        \"rectangle\": \"مستطيل\",\n        \"circle\": \"دائري\",\n        \"square\": \"مربع\",\n        \"status\": \"الحالة\",\n        \"position\": \"الموقع\",\n        \"size\": \"الحجم\",\n        \"width\": \"العرض\",\n        \"height\": \"الطول\",\n        \"current_bill\": \"الفاتورة الحالية\",\n        \"total_amount\": \"المبلغ الإجمالي\",\n        \"last_updated\": \"آخر تحديث\",\n        \"order_items\": \"عناصر الطلب\",\n        \"started_at\": \"بدأ في\",\n        \"no_active_order\": \"لم يتم العثور على طلب نشط\",\n        \"loading_table_order\": \"جاري تحميل طلب الطاولة...\",\n        \"table_name_title\": \"يتم إدارة أسماء الطاولات من لوحة التحكم\",\n        \"capacity_placeholder\": \"أدخل 1-20\",\n        \"capacity_range_hint\": \"النطاق الصالح: 1-20 شخص\",\n        \"editing_layout_hint_title\": \"تعديل المخطط\",\n        \"drag_tables_hint\": \"• اسحب الطاولات لتغيير موضعها\",\n        \"autosave_hint\": \"• يتم حفظ التغييرات تلقائيًا\",\n        \"zoom_pan_hint\": \"استخدم التمرير للتكبير • اسحب الخلفية للتحريك\"\n    },\n    \"footer\": {\n        \"pos\": \"نقطة البيع\",\n        \"table\": \"طاولة\",\n        \"orders\": \"الطلبات\"\n    },\n    \"product_dialog\": {\n        \"special_instructions\": \"تعليمات خاصة\",\n        \"special_instructions_placeholder\": \"أضف أي تعليمات خاصة أو ملاحظات لهذا العنصر...\",\n        \"quantity\": \"الكمية\",\n        \"variants\": \"الخيارات\",\n        \"addons\": \"الإضافات\",\n        \"loading_addons\": \"جاري تحميل الإضافات...\",\n        \"no_addons\": \"لا توجد إضافات\",\n        \"total\": \"الإجمالي\",\n        \"update_order\": \"تحديث الطلب\",\n        \"add_to_order\": \"إضافة إلى الطلب\"\n    },\n    \"order_types\": {\n        \"dine_in\": \"طلب محلي\",\n        \"take_away\": \"سفري\",\n        \"delivery\": \"توصيل\",\n        \"phone_in\": \"طلب هاتفي\",\n        \"aggregators\": \"منصات تجميع\"\n    },\n    \"order_status_types\": {\n        \"draft\": \"مسودة\",\n        \"unbilled\": \"غير مفوتر\",\n        \"recently_paid\": \"مدفوع مؤخراً\",\n        \"paid\": \"مدفوع\",\n        \"consolidated\": \"مجمع\",\n        \"return\": \"مرتجع\"\n    }\n}"
  },
  {
    "path": "pos/src/i18n/locales/en.json",
    "content": "{\n  \"common\": {\n    \"loading\": \"Loading...\",\n    \"cancel\": \"Cancel\",\n    \"save\": \"Save\",\n    \"apply\": \"Apply\",\n    \"change\": \"Change\",\n    \"all\": \"All\",\n    \"no_options\": \"No options\",\n    \"searching\": \"Searching...\",\n    \"no_items_found\": \"No items found\",\n    \"try_adjusting_filters\": \"Try adjusting your filters or search term\",\n    \"loading_tables\": \"Loading tables...\",\n    \"loading_menu_items\": \"Loading menu items...\",\n    \"error_loading_menu_items\": \"Error loading menu items\",\n    \"loading_rooms\": \"Loading rooms...\",\n    \"no_rooms_found\": \"No rooms found\",\n    \"no_tables_found\": \"No tables found\",\n    \"select_table_title\": \"Select Table\",\n    \"checking_pos_status\": \"Checking POS status...\",\n    \"cancelling\": \"Cancelling...\",\n    \"confirm_cancel\": \"Confirm Cancel\",\n    \"loading_ury_pos\": \"Loading URY POS...\",\n    \"please_wait_setup\": \"Please wait while we set things up\"\n  },\n  \"header\": {\n    \"search_placeholder_default\": \"Search orders, menu items, or customers...\",\n    \"search_placeholder_orders\": \"Search Orders\",\n    \"search_placeholder_menu\": \"Search Menu\",\n    \"switch_to_desk\": \"Switch To Desk\",\n    \"clear_cache\": \"Clear Cache\",\n    \"logout\": \"Logout\"\n  },\n  \"menu\": {\n    \"special_items\": \"Special Items\"\n  },\n  \"pos_sidebar\": {\n    \"categories\": \"Categories\",\n    \"all_items\": \"All Items\"\n  },\n  \"cart\": {\n    \"empty_title\": \"Your cart is empty\",\n    \"empty_subtitle\": \"Add items to get started with your order\",\n    \"click_to_add\": \"Click items to add them\",\n    \"double_click_hint\": \"Double-click for customization options\",\n    \"clear_cart\": \"Clear cart\",\n    \"total\": \"Total\",\n    \"add_new_order\": \"Add New Order\",\n    \"update_order\": \"Update Order\",\n    \"updating_order\": \"Updating Order...\",\n    \"processing_order\": \"Processing Order...\",\n    \"loading_order\": \"Loading order details...\",\n    \"edit_item\": \"Edit item\",\n    \"add_comment\": \"Add comment\",\n    \"edit_comment\": \"Edit comment\"\n  },\n  \"order\": {\n    \"select_to_view\": \"Select an order to view details\",\n    \"items_title\": \"Order Items\",\n    \"taxes_charges\": \"Taxes & Charges\",\n    \"cancel_order\": \"Cancel order\",\n    \"enter_cancel_reason\": \"Enter cancel reason\",\n    \"print\": \"Print\",\n    \"payment\": \"Payment\"\n  },\n  \"payment\": {\n    \"title\": \"Payment\",\n    \"apply_discount\": \"Apply Discount\",\n    \"discount_placeholder\": \"Enter %\",\n    \"payment_methods\": \"Payment Methods\",\n    \"amount_placeholder\": \"Amount\",\n    \"total_entered\": \"Total Entered\",\n    \"order_summary\": \"Order Summary\",\n    \"subtotal\": \"Subtotal\",\n    \"discount\": \"Discount\",\n    \"adjustment\": \"Adjustment\",\n    \"total\": \"Total\",\n    \"pay_button\": \"Pay {{amount}}\",\n    \"processing\": \"Processing...\"\n  },\n  \"pos\": {\n    \"not_opened_title\": \"POS Not Opened\",\n    \"not_opened_message\": \"Please open POS Entry to continue using the system.\",\n    \"not_closed_title\": \"Previous POS Not Closed\",\n    \"not_closed_message\": \"Please close the previous POS Entry to continue.\",\n    \"reload_page\": \"Reload Page\",\n    \"switch_to_desk\": \"Switch to Desk\"\n  },\n  \"customer\": {\n    \"search_placeholder\": \"Search customer...\",\n    \"type_to_search\": \"Please type to search...\",\n    \"no_customers_found\": \"No customers found\",\n    \"add_new\": \"Add New Customer\",\n    \"add_with_name\": \"Add \\\"{{name}}\\\"...\",\n    \"failed_search\": \"Failed to search customers\",\n    \"add_customer_title\": \"Add New Customer\",\n    \"name_label\": \"Name\",\n    \"name_required\": \"Name is required\",\n    \"phone_label\": \"Phone\",\n    \"phone_required\": \"Phone is required\",\n    \"customer_group_label\": \"Customer Group\",\n    \"territory_label\": \"Territory\",\n    \"select_group\": \"Select group\",\n    \"select_territory\": \"Select territory\",\n    \"adding\": \"Adding Customer...\",\n    \"add_button\": \"Add Customer\",\n    \"failed_create\": \"Failed to create customer. Please try again.\"\n  },\n  \"comment\": {\n    \"title\": \"Order Comments\",\n    \"label\": \"Add comments for this order\",\n    \"placeholder\": \"Enter any special instructions or comments...\",\n    \"save_button\": \"Save Comment\"\n  },\n  \"errors\": {\n    \"pos_profile_not_found\": \"POS Profile not found\",\n    \"pos_profile_not_loaded\": \"POS profile not loaded yet\",\n    \"user_not_logged_in\": \"User not logged in\",\n    \"failed_load_rooms\": \"Failed to load rooms\",\n    \"failed_load_tables\": \"Failed to load tables\",\n    \"select_aggregator\": \"Please select an aggregator before proceeding\",\n    \"select_customer\": \"Please select a customer before proceeding\",\n    \"select_table\": \"Please select a table for {{order_type}} orders\",\n    \"failed_logout\": \"Failed to logout. Please try again.\",\n    \"failed_cancel_order\": \"Failed to cancel order\",\n    \"failed_edit_order\": \"Failed to edit order\",\n    \"failed_fetch_invoices\": \"Failed to fetch POS invoices\",\n    \"failed_fetch_order\": \"Failed to fetch order details\",\n    \"failed_print\": \"Failed to print order\",\n    \"failed_process_order\": \"Failed to process order\",\n    \"no_active_order\": \"No active order found for this table\",\n    \"qz_host_not_set\": \"QZ host is not set\",\n    \"print_failed\": \"Print failed: {{reason}}\",\n    \"invalid_discount\": \"Please enter a valid discount value\",\n    \"discount_exceeds_max\": \"Percentage discount cannot exceed 100%\",\n    \"please_print_first\": \"Please print invoice before making payment\",\n    \"enter_cancel_reason\": \"Please enter a reason for cancellation.\",\n    \"dine_in_restricted\": \"Dine In is not available for your role\"\n  },\n  \"success\": {\n    \"order_cancelled\": \"Order cancelled successfully\",\n    \"order_created\": \"Order created successfully\",\n    \"order_updated\": \"Order updated successfully\",\n    \"printed\": \"Printed successfully\",\n    \"payment_successful\": \"Payment successful\",\n    \"order_moved_to_draft\": \"Order moved to Draft after printing.\"\n  },\n  \"orders\": {\n    \"no_orders_found\": \"No orders found\",\n    \"click_to_view\": \"Click on any order card to view its details\",\n    \"status_title\": \"Order Status\",\n    \"pagination\": {\n      \"previous\": \"Previous\",\n      \"next\": \"Next\",\n      \"page\": \"Page {{number}}\"\n    }\n  },\n  \"tables\": {\n    \"layout_view\": \"Layout View\",\n    \"grid_view\": \"Grid View\",\n    \"edit_layout\": \"Edit Layout\",\n    \"finish_editing\": \"Finish Editing\",\n    \"layout\": \"Layout\",\n    \"available\": \"Available\",\n    \"occupied\": \"Occupied\",\n    \"room\": \"Room\",\n    \"seats\": \"Seats\",\n    \"tap_to_start\": \"Tap to start a new dine-in order\",\n    \"no_tables_found\": \"No tables found for this room\",\n    \"edit_settings\": \"Edit Table Settings\",\n    \"table_info\": \"Table Info\",\n    \"table_name\": \"Table Name\",\n    \"capacity\": \"Capacity\",\n    \"shape\": \"Shape\",\n    \"rectangle\": \"Rectangle\",\n    \"circle\": \"Circle\",\n    \"square\": \"Square\",\n    \"status\": \"Status\",\n    \"position\": \"Position\",\n    \"size\": \"Size\",\n    \"width\": \"Width\",\n    \"height\": \"Height\",\n    \"current_bill\": \"Current Bill\",\n    \"total_amount\": \"Total Amount\",\n    \"last_updated\": \"Last Updated\",\n    \"order_items\": \"Order Items\",\n    \"started_at\": \"Started at:\",\n    \"no_active_order\": \"No active order found\",\n    \"loading_table_order\": \"Loading table order...\",\n    \"table_name_title\": \"Table names are managed in backend\",\n    \"capacity_placeholder\": \"Enter 1-20\",\n    \"capacity_range_hint\": \"Valid range: 1-20 pax\",\n    \"editing_layout_hint_title\": \"Editing Layout\",\n    \"drag_tables_hint\": \"• Drag tables to reposition\",\n    \"autosave_hint\": \"• Changes autosave\",\n    \"zoom_pan_hint\": \"Use Scroll to zoom • Drag background to pan\"\n  },\n  \"footer\": {\n    \"pos\": \"POS\",\n    \"table\": \"Table\",\n    \"orders\": \"Orders\"\n  },\n  \"product_dialog\": {\n    \"special_instructions\": \"Special Instructions\",\n    \"special_instructions_placeholder\": \"Add any special instructions or notes for this item...\",\n    \"quantity\": \"Quantity\",\n    \"variants\": \"Variants\",\n    \"addons\": \"Add-ons\",\n    \"loading_addons\": \"Loading add-ons...\",\n    \"no_addons\": \"No add ons\",\n    \"total\": \"Total\",\n    \"update_order\": \"Update Order\",\n    \"add_to_order\": \"Add to Order\"\n  },\n  \"order_types\": {\n    \"dine_in\": \"Dine In\",\n    \"take_away\": \"Take Away\",\n    \"delivery\": \"Delivery\",\n    \"phone_in\": \"Phone In\",\n    \"aggregators\": \"Aggregators\"\n  },\n  \"order_status_types\": {\n    \"draft\": \"Draft\",\n    \"unbilled\": \"Unbilled\",\n    \"recently_paid\": \"Recently Paid\",\n    \"paid\": \"Paid\",\n    \"consolidated\": \"Consolidated\",\n    \"return\": \"Return\"\n  }\n}\n"
  },
  {
    "path": "pos/src/i18n/locales/fr.json",
    "content": "{\n  \"common\": {\n    \"loading\": \"Chargement...\",\n    \"cancel\": \"Annuler\",\n    \"save\": \"Enregistrer\",\n    \"apply\": \"Appliquer\",\n    \"change\": \"Modifier\",\n    \"all\": \"Tout\",\n    \"no_options\": \"Aucune option\",\n    \"searching\": \"Recherche...\",\n    \"no_items_found\": \"Aucun article trouvé\",\n    \"try_adjusting_filters\": \"Essayez de modifier vos filtres ou terme de recherche\",\n    \"loading_tables\": \"Chargement des tables...\",\n    \"loading_menu_items\": \"Chargement des articles du menu...\",\n    \"error_loading_menu_items\": \"Erreur lors du chargement des articles du menu\",\n    \"loading_rooms\": \"Chargement des salles...\",\n    \"no_rooms_found\": \"Aucune salle trouvée\",\n    \"no_tables_found\": \"Aucune table trouvée\",\n    \"select_table_title\": \"Sélectionner une table\",\n    \"checking_pos_status\": \"Vérification de l'état de la POS...\",\n    \"cancelling\": \"Annulation en cours...\",\n    \"confirm_cancel\": \"Confirmer l'annulation\",\n    \"loading_ury_pos\": \"Chargement de URY POS...\",\n    \"please_wait_setup\": \"Veuillez patienter pendant que nous préparons tout\"\n  },\n  \"header\": {\n    \"search_placeholder_default\": \"Rechercher commandes, articles ou clients...\",\n    \"search_placeholder_orders\": \"Rechercher des commandes\",\n    \"search_placeholder_menu\": \"Rechercher dans le menu\",\n    \"switch_to_desk\": \"Accéder au bureau\",\n    \"clear_cache\": \"Vider le cache\",\n    \"logout\": \"Déconnexion\"\n  },\n  \"menu\": {\n    \"special_items\": \"Plats spéciaux\"\n  },\n  \"pos_sidebar\": {\n    \"categories\": \"Catégories\",\n    \"all_items\": \"Tous les articles\"\n  },\n  \"cart\": {\n    \"empty_title\": \"Votre panier est vide\",\n    \"empty_subtitle\": \"Ajoutez des articles pour commencer votre commande\",\n    \"click_to_add\": \"Cliquez sur les articles pour les ajouter\",\n    \"double_click_hint\": \"Double-cliquez pour les options de personnalisation\",\n    \"clear_cart\": \"Vider le panier\",\n    \"total\": \"Total\",\n    \"add_new_order\": \"Nouvelle commande\",\n    \"update_order\": \"Mettre à jour la commande\",\n    \"updating_order\": \"Mise à jour...\",\n    \"processing_order\": \"Traitement en cours...\",\n    \"loading_order\": \"Chargement des détails de la commande...\",\n    \"edit_item\": \"Modifier l'article\",\n    \"add_comment\": \"Ajouter un commentaire\",\n    \"edit_comment\": \"Modifier le commentaire\"\n  },\n  \"order\": {\n    \"select_to_view\": \"Sélectionnez une commande pour voir les détails\",\n    \"items_title\": \"Articles de la commande\",\n    \"taxes_charges\": \"Taxes et frais\",\n    \"cancel_order\": \"Annuler la commande\",\n    \"enter_cancel_reason\": \"Saisir le motif d'annulation\",\n    \"print\": \"Imprimer\",\n    \"payment\": \"Paiement\"\n  },\n  \"payment\": {\n    \"title\": \"Paiement\",\n    \"apply_discount\": \"Appliquer une remise\",\n    \"discount_placeholder\": \"Entrer %\",\n    \"payment_methods\": \"Modes de paiement\",\n    \"amount_placeholder\": \"Montant\",\n    \"total_entered\": \"Total saisi\",\n    \"order_summary\": \"Résumé de la commande\",\n    \"subtotal\": \"Sous-total\",\n    \"discount\": \"Remise\",\n    \"adjustment\": \"Ajustement\",\n    \"total\": \"Total\",\n    \"pay_button\": \"Payer {{amount}}\",\n    \"processing\": \"Traitement...\"\n  },\n  \"pos\": {\n    \"not_opened_title\": \"PDV non ouvert\",\n    \"not_opened_message\": \"Veuillez ouvrir une entrée PDV pour continuer.\",\n    \"not_closed_title\": \"PDV précédent non fermé\",\n    \"not_closed_message\": \"Veuillez fermer l'entrée PDV précédente pour continuer.\",\n    \"reload_page\": \"Recharger la page\",\n    \"switch_to_desk\": \"Accéder au bureau\"\n  },\n  \"customer\": {\n    \"search_placeholder\": \"Rechercher un client...\",\n    \"type_to_search\": \"Tapez pour rechercher...\",\n    \"no_customers_found\": \"Aucun client trouvé\",\n    \"add_new\": \"Ajouter un nouveau client\",\n    \"add_with_name\": \"Ajouter \\\"{{name}}\\\"...\",\n    \"failed_search\": \"Échec de la recherche de clients\",\n    \"add_customer_title\": \"Ajouter un nouveau client\",\n    \"name_label\": \"Nom\",\n    \"name_required\": \"Le nom est obligatoire\",\n    \"phone_label\": \"Téléphone\",\n    \"phone_required\": \"Le téléphone est obligatoire\",\n    \"customer_group_label\": \"Groupe client\",\n    \"territory_label\": \"Territoire\",\n    \"select_group\": \"Sélectionner un groupe\",\n    \"select_territory\": \"Sélectionner un territoire\",\n    \"adding\": \"Ajout du client...\",\n    \"add_button\": \"Ajouter le client\",\n    \"failed_create\": \"Échec de la création du client. Veuillez réessayer.\"\n  },\n  \"comment\": {\n    \"title\": \"Commentaires de la commande\",\n    \"label\": \"Ajouter des commentaires pour cette commande\",\n    \"placeholder\": \"Entrez des instructions spéciales ou des commentaires...\",\n    \"save_button\": \"Enregistrer le commentaire\"\n  },\n  \"errors\": {\n    \"pos_profile_not_found\": \"Profil PDV introuvable\",\n    \"pos_profile_not_loaded\": \"Le profil PDV n'est pas encore chargé\",\n    \"user_not_logged_in\": \"Utilisateur non connecté\",\n    \"failed_load_rooms\": \"Échec du chargement des salles\",\n    \"failed_load_tables\": \"Échec du chargement des tables\",\n    \"select_aggregator\": \"Veuillez sélectionner un agrégateur avant de continuer\",\n    \"select_customer\": \"Veuillez sélectionner un client avant de continuer\",\n    \"select_table\": \"Veuillez sélectionner une table pour les commandes {{order_type}}\",\n    \"failed_logout\": \"Échec de la déconnexion. Veuillez réessayer.\",\n    \"failed_cancel_order\": \"Échec de l'annulation de la commande\",\n    \"failed_edit_order\": \"Échec de la modification de la commande\",\n    \"failed_fetch_invoices\": \"Échec du chargement des factures PDV\",\n    \"failed_fetch_order\": \"Échec du chargement des détails de la commande\",\n    \"failed_print\": \"Échec de l'impression de la commande\",\n    \"failed_process_order\": \"Échec du traitement de la commande\",\n    \"no_active_order\": \"Aucune commande active trouvée pour cette table\",\n    \"qz_host_not_set\": \"L'hôte QZ n'est pas configuré\",\n    \"print_failed\": \"Échec d'impression : {{reason}}\",\n    \"invalid_discount\": \"Veuillez entrer une valeur de remise valide\",\n    \"discount_exceeds_max\": \"La remise en pourcentage ne peut pas dépasser 100 %\",\n    \"please_print_first\": \"Veuillez imprimer la facture avant d'effectuer le paiement\",\n    \"enter_cancel_reason\": \"Veuillez entrer un motif d'annulation.\",\n    \"dine_in_restricted\": \"La consommation sur place n'est pas disponible pour votre rôle\"\n  },\n  \"success\": {\n    \"order_cancelled\": \"Commande annulée avec succès\",\n    \"order_created\": \"Commande créée avec succès\",\n    \"order_updated\": \"Commande mise à jour avec succès\",\n    \"printed\": \"Imprimé avec succès\",\n    \"payment_successful\": \"Paiement réussi\",\n    \"order_moved_to_draft\": \"Commande déplacée en brouillon après impression.\"\n  },\n  \"orders\": {\n    \"no_orders_found\": \"Aucune commande trouvée\",\n    \"click_to_view\": \"Cliquez sur n'importe quel badge de commande pour voir ses détails\",\n    \"status_title\": \"Statut de la commande\",\n    \"pagination\": {\n      \"previous\": \"Précédent\",\n      \"next\": \"Suivant\",\n      \"page\": \"Page {{number}}\"\n    }\n  },\n  \"tables\": {\n    \"layout_view\": \"Vue de mise en page\",\n    \"grid_view\": \"Vue en grille\",\n    \"edit_layout\": \"Modifier le plan\",\n    \"finish_editing\": \"Terminer la modification\",\n    \"layout\": \"Plan\",\n    \"available\": \"Disponible\",\n    \"occupied\": \"Occupé\",\n    \"room\": \"Salle\",\n    \"seats\": \"Sièges\",\n    \"tap_to_start\": \"Appuyez pour commencer une nouvelle commande sur place\",\n    \"no_tables_found\": \"Aucune table trouvée pour cette salle\",\n    \"edit_settings\": \"Modifier les paramètres de la table\",\n    \"table_info\": \"Informations sur la table\",\n    \"table_name\": \"Nom de la table\",\n    \"capacity\": \"Capacité\",\n    \"shape\": \"Forme\",\n    \"rectangle\": \"Rectangle\",\n    \"circle\": \"Cercle\",\n    \"square\": \"Carré\",\n    \"status\": \"Statut\",\n    \"position\": \"Position\",\n    \"size\": \"Taille\",\n    \"width\": \"Largeur\",\n    \"height\": \"Hauteur\",\n    \"current_bill\": \"Facture actuelle\",\n    \"total_amount\": \"Montant total\",\n    \"last_updated\": \"Dernière mise à jour\",\n    \"order_items\": \"Articles de la commande\",\n    \"started_at\": \"Démarré à :\",\n    \"no_active_order\": \"Aucune commande active trouvée\",\n    \"loading_table_order\": \"Chargement de la commande de la table...\",\n    \"table_name_title\": \"Les noms des tables sont gérés dans le backend\",\n    \"capacity_placeholder\": \"Entrez 1-20\",\n    \"capacity_range_hint\": \"Plage valide : 1-20 pax\",\n    \"editing_layout_hint_title\": \"Modification du plan\",\n    \"drag_tables_hint\": \"• Faites glisser les tables pour les repositionner\",\n    \"autosave_hint\": \"• Les modifications sont enregistrées automatiquement\",\n    \"zoom_pan_hint\": \"Utilisez le défilement pour zoomer • Faites glisser l'arrière-plan pour déplacer\"\n  },\n  \"footer\": {\n    \"pos\": \"PDV\",\n    \"table\": \"Table\",\n    \"orders\": \"Commandes\"\n  },\n  \"product_dialog\": {\n    \"special_instructions\": \"Instructions spéciales\",\n    \"special_instructions_placeholder\": \"Ajoutez des instructions spéciales ou des notes pour cet article...\",\n    \"quantity\": \"Quantité\",\n    \"variants\": \"Variantes\",\n    \"addons\": \"Suppléments\",\n    \"loading_addons\": \"Chargement des suppléments...\",\n    \"no_addons\": \"Aucun supplément\",\n    \"total\": \"Total\",\n    \"update_order\": \"Mettre à jour la commande\",\n    \"add_to_order\": \"Ajouter à la commande\"\n  },\n  \"order_types\": {\n    \"dine_in\": \"Sur place\",\n    \"take_away\": \"À emporter\",\n    \"delivery\": \"Livraison\",\n    \"phone_in\": \"Commande par téléphone\",\n    \"aggregators\": \"Agrégateurs\"\n  },\n  \"order_status_types\": {\n    \"draft\": \"Brouillon\",\n    \"unbilled\": \"Non facturé\",\n    \"recently_paid\": \"Payé récemment\",\n    \"paid\": \"Payé\",\n    \"consolidated\": \"Consolidé\",\n    \"return\": \"Retour\"\n  }\n}\n"
  },
  {
    "path": "pos/src/i18n/resolve-language.ts",
    "content": "import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from './config';\n\n/**\n * Resolves the active language using the following priority:\n * 1. frappe.boot.lang (Frappe site config / user preference)\n * 2. localStorage key 'ury_language'\n * 3. DEFAULT_LANGUAGE ('en')\n */\nexport function resolveLanguage(): string {\n  // 1. Frappe boot object\n  const frappeLang: string | undefined =\n    (window as any)?.frappe?.boot?.lang;\n  if (frappeLang && SUPPORTED_LANGUAGES[frappeLang]) {\n    return frappeLang;\n  }\n\n  // 2. Local storage override\n  const storedLang = localStorage.getItem('ury_language');\n  if (storedLang && SUPPORTED_LANGUAGES[storedLang]) {\n    return storedLang;\n  }\n\n  return DEFAULT_LANGUAGE;\n}\n"
  },
  {
    "path": "pos/src/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 84% 4.9%;\n    --primary: 221.2 83.2% 53.3%;\n    --primary-foreground: 210 40% 98%;\n    --primary-50: 226 100% 97%;\n    --primary-100: 224 96% 90%;\n    --primary-200: 223 94% 82%;\n    --primary-300: 222 91% 69%;\n    --primary-400: 221 83% 53%;\n    --primary-500: 221 76% 47%;\n    --primary-600: 221 69% 42%;\n    --primary-700: 221 61% 36%;\n    --primary-800: 221 53% 30%;\n    --primary-900: 221 45% 24%;\n    --primary-950: 221 37% 18%;\n    --secondary: 210 40% 96%;\n    --secondary-foreground: 222.2 84% 4.9%;\n    --muted: 210 40% 96%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n    --accent: 210 40% 96%;\n    --accent-foreground: 222.2 84% 4.9%;\n    --accent-50: 355 100% 97%;\n    --accent-100: 353 96% 90%;\n    --accent-200: 352 94% 82%;\n    --accent-300: 351 91% 69%;\n    --accent-400: 350 83% 53%;\n    --accent-500: 350 76% 47%;\n    --accent-600: 350 69% 42%;\n    --accent-700: 350 61% 36%;\n    --accent-800: 350 53% 30%;\n    --accent-900: 350 45% 24%;\n    --accent-950: 350 37% 18%;\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n    --ring: 221.2 83.2% 53.3%;\n    --radius: 0.5rem;\n    --gray-50: 240 20% 99%;\n    --gray-100: 240 20% 97%;\n    --gray-200: 240 15% 92%;\n    --gray-300: 240 10% 85%;\n    --gray-400: 240 8% 70%;\n    --gray-500: 240 6% 50%;\n    --gray-600: 240 5% 40%;\n    --gray-700: 240 4% 30%;\n    --gray-800: 240 3% 20%;\n    --gray-900: 240 2% 10%;\n    --gray-950: 240 1% 5%;\n    --white: 0 0% 100%;\n    --black: 0 0% 0%;\n    --order-panel-width: 24rem;\n    --badge-min-width: 1.5rem;\n    --dialog-max-width: 90rem;\n    --dialog-max-height: 90vh;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n    --card: 222.2 84% 4.9%;\n    --card-foreground: 210 40% 98%;\n    --popover: 222.2 84% 4.9%;\n    --popover-foreground: 210 40% 98%;\n    --primary: 217.2 91.2% 59.8%;\n    --primary-foreground: 222.2 84% 4.9%;\n    --primary-50: 226 100% 10%;\n    --primary-100: 224 96% 15%;\n    --primary-200: 223 94% 20%;\n    --primary-300: 222 91% 25%;\n    --primary-400: 221 83% 30%;\n    --primary-500: 221 76% 35%;\n    --primary-600: 221 69% 40%;\n    --primary-700: 221 61% 45%;\n    --primary-800: 221 53% 50%;\n    --primary-900: 221 45% 55%;\n    --primary-950: 221 37% 60%;\n    --secondary: 217.2 32.6% 17.5%;\n    --secondary-foreground: 210 40% 98%;\n    --muted: 217.2 32.6% 17.5%;\n    --muted-foreground: 215 20.2% 65.1%;\n    --accent: 217.2 32.6% 17.5%;\n    --accent-foreground: 210 40% 98%;\n    --accent-50: 355 100% 10%;\n    --accent-100: 353 96% 15%;\n    --accent-200: 352 94% 20%;\n    --accent-300: 351 91% 25%;\n    --accent-400: 350 83% 30%;\n    --accent-500: 350 76% 35%;\n    --accent-600: 350 69% 40%;\n    --accent-700: 350 61% 45%;\n    --accent-800: 350 53% 50%;\n    --accent-900: 350 45% 55%;\n    --accent-950: 350 37% 60%;\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 210 40% 98%;\n    --border: 217.2 32.6% 17.5%;\n    --input: 217.2 32.6% 17.5%;\n    --ring: 224.3 76.3% 94.1%;\n    --gray-50: 240 20% 10%;\n    --gray-100: 240 20% 15%;\n    --gray-200: 240 15% 20%;\n    --gray-300: 240 10% 25%;\n    --gray-400: 240 8% 30%;\n    --gray-500: 240 6% 35%;\n    --gray-600: 240 5% 40%;\n    --gray-700: 240 4% 45%;\n    --gray-800: 240 3% 50%;\n    --gray-900: 240 2% 55%;\n    --gray-950: 240 1% 60%;\n    --white: 0 0% 100%;\n    --black: 0 0% 0%;\n    --order-panel-width: 24rem;\n    --badge-min-width: 1.5rem;\n    --dialog-max-width: 90rem;\n    --dialog-max-height: 90vh;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n"
  },
  {
    "path": "pos/src/lib/aggregator-api.ts",
    "content": "import { call } from './frappe-sdk';\n\nexport interface Aggregator {\n  customer: string;\n}\n\nexport interface GetAggregatorsResponse {\n  message: Aggregator[];\n}\n\nexport async function getAggregators(): Promise<Aggregator[]> {\n  try {\n    const response = await call.get<GetAggregatorsResponse>(\n      'ury.ury_pos.api.getAggregator'\n    );\n    return response.message;\n  } catch (error: any) {\n    if (error._server_messages) {\n      const messages = JSON.parse(error._server_messages);\n      const message = JSON.parse(messages[0]);\n      throw new Error(message.message);\n    }\n    console.error('Failed to fetch aggregators:', error);\n    throw error;\n  }\n} "
  },
  {
    "path": "pos/src/lib/auth-api.ts",
    "content": "import { call, db, auth } from './frappe-sdk';\n\ntype LoggedUserResponse = string | null;\n\ninterface UserDoc {\n  name: string;\n  full_name: string;\n  roles: Array<{\n    name: string;\n    role: string;\n    parent: string;\n  }>;\n}\n\nexport const getLoggedUser = async (): Promise<LoggedUserResponse> => {\n  try {\n    const response = await auth.getLoggedInUser();\n    return response as LoggedUserResponse;\n  } catch (error) {\n    console.error('Error getting logged user:', error);\n    return null;\n  }\n};\n\nexport const getUserRoles = async (email: string): Promise<{ roles: string[]; full_name: string }> => {\n  try {\n    // Get user details using db.getDoc\n    const userDoc = await db.getDoc<UserDoc>('User', email);\n    \n    if (!userDoc || !userDoc.roles) {\n      return { roles: [], full_name: '' };\n    }\n\n    // Extract role names and full_name from the user doc\n    return {\n      roles: userDoc.roles.map(role => role.role),\n      full_name: userDoc.full_name\n    };\n  } catch (error) {\n    console.error('Error getting user details:', error);\n    return { roles: [], full_name: '' };\n  }\n};\n\nexport const logout = async () => {\n  try {\n    return auth.logout();\n  }catch(e){\n    console.error('Error logging out:', e);\n    return false;\n  }\n}"
  },
  {
    "path": "pos/src/lib/customer-api.ts",
    "content": "import { DOCTYPES } from '../data/doctypes';\nimport { db, call } from './frappe-sdk';\n\nexport interface Customer {\n  name: string;\n  owner: string;\n  creation: string;\n  modified: string;\n  modified_by: string;\n  docstatus: number;\n  idx: number;\n  naming_series: string;\n  customer_name: string;\n  customer_type: string;\n  mobile_number: string;\n  customer_group: string;\n  territory: string;\n  is_internal_customer: number;\n  language: string;\n  default_commission_rate: number;\n  so_required: number;\n  dn_required: number;\n  is_frozen: number;\n  disabled: number;\n  doctype: string;\n  companies: any[];\n  credit_limits: any[];\n  accounts: any[];\n  sales_team: any[];\n  portal_users: any[];\n}\n\nexport interface CreateCustomerData {\n  customer_name: string;\n  mobile_number: string;\n  customer_group?: string;\n  territory?: string;\n}\n\nexport interface CreateCustomerResponse {\n  data: CreateCustomerData;\n  _server_messages?: string;\n}\n\n\nexport async function getCustomerGroups() {\n  const groups = await db.getDocList(DOCTYPES.CUSTOMER_GROUP, {\n    fields: ['name'],\n    limit: \"*\" as unknown as number,\n    orderBy: {\n      field: 'name',\n      order: 'asc',\n    },\n  });\n  return groups;\n}\n\nexport async function getCustomerTerritories() {\n  const territories = await db.getDocList(DOCTYPES.CUSTOMER_TERRITORY, {\n    fields: ['name'],\n    limit: \"*\" as unknown as number,\n    orderBy: {\n      field: 'name',\n      order: 'asc',\n    },\n  });\n  return territories;\n}\n\nexport async function addCustomer(\n  customerData: CreateCustomerData\n): Promise<CreateCustomerResponse> {\n  try {\n    const response = await call.post('ury.ury_pos.api.create_customer', customerData);\n    const msg = response.message;\n    if (!msg || msg.status !== \"success\") {\n      throw new Error(\"Failed to create Customer,API Response error\");\n    }\n    return {\n      data: {\n        customer_name: msg.customer_name,\n        mobile_number: msg.mobile_number,\n        customer_group: msg.customer_group,\n        territory: msg.territory\n      }\n    };\n\n  } catch (error) {\n    console.error('Error creating customer:', error);\n    throw error;\n  }\n}\n\nfunction getscramblePattern(text: string) {\n  return `%${text.split(\"\").join(\"%\")}%`;\n}\n\nexport async function searchCustomers(search: string, limit = 5) {\n  if (!search.trim()) return [];\n\n  const pattern = getscramblePattern(search);\n\n  try {\n    const res = await db.getDocList(DOCTYPES.CUSTOMER, {\n      fields: [\"name\", \"customer_name\", \"mobile_number\"],\n      orFilters: [\n        [\"customer_name\", \"like\", pattern],\n        [\"mobile_number\", \"like\", pattern],\n        [\"name\", \"like\", pattern],\n      ],\n      limit,\n      limit_start: 0,\n    });\n\n    return res.map((doc: any) => ({\n      ...doc,\n      content: `Customer Name : ${doc.customer_name ?? \"\"} | Mobile Number : ${doc.mobile_number ?? \"\"}`,\n    }));\n  } catch (error) {\n    console.error(\"Customer search error:\", error);\n    throw error;\n  }\n}\n"
  },
  {
    "path": "pos/src/lib/frappe-sdk.ts",
    "content": "import { FrappeApp } from \"frappe-js-sdk\";\n\nconst frappe = new FrappeApp(import.meta.env.VITE_FRAPPE_BASE_URL);\n\nexport const call = frappe.call();\nexport const db = frappe.db();\nexport const auth = frappe.auth();"
  },
  {
    "path": "pos/src/lib/invoice-api.ts",
    "content": "import { call } from './frappe-sdk';\nimport { OrderType } from '../data/order-types';\n\nexport interface POSInvoice {\n  name: string;\n  invoice_printed: number;\n  grand_total: number;\n  restaurant_table: string | null;\n  cashier: string;\n  waiter: string;\n  net_total: number;\n  posting_time: string;\n  total_taxes_and_charges: number;\n  customer: string;\n  status: 'Draft' | 'Unbilled' | 'Recently Paid' | 'Paid' | 'Consolidated' | 'Return';\n  mobile_number: string;\n  posting_date: string;\n  rounded_total: number;\n  order_type: OrderType;\n}\n\nexport interface POSInvoiceItem {\n  item_name: string;\n  qty: number;\n  amount: number;\n}\n\nexport interface POSInvoiceTax {\n  description: string;\n  rate: number;\n}\n\ninterface GetPOSInvoicesResponse {\n  message: {\n    data: POSInvoice[];\n    next: boolean;\n  };\n}\n\ninterface GetPOSInvoicesParams {\n  status: POSInvoice['status'];\n  limit?: number;\n  limit_start?: number;\n  paid_limit?: number;\n}\n\ninterface GetPOSInvoiceItemsResponse {\n  message: [POSInvoiceItem[], POSInvoiceTax[]];\n}\n\nexport async function getPOSInvoices({ \n  status, \n  limit, \n  limit_start,\n  paid_limit\n}: GetPOSInvoicesParams) {\n  try {\n    // Use paid_limit as the limit for Recently Paid status\n    const actualLimit = status === 'Recently Paid' && paid_limit ? paid_limit : limit;\n    \n    const response = await call.get<GetPOSInvoicesResponse>(\n      'ury.ury_pos.api.getPosInvoice',\n      {\n        status,\n        limit: actualLimit,\n        limit_start\n      }\n    );\n\n    return {\n      invoices: response.message.data,\n      hasMore: response.message.next\n    };\n  } catch (error) {\n    console.error('Error fetching POS invoices:', error);\n    throw new Error('Failed to fetch POS invoices');\n  }\n}\n\nexport async function getPOSInvoiceItems(invoiceId: string) {\n  try {\n    const response = await call.get<GetPOSInvoiceItemsResponse>(\n      'ury.ury_pos.api.getPosInvoiceItems',\n      {\n        invoice: invoiceId\n      }\n    );\n\n    return {\n      items: response.message[0],\n      taxes: response.message[1]\n    };\n  } catch (error) {\n    console.error('Error fetching POS invoice items:', error);\n    throw new Error('Failed to fetch POS invoice items');\n  }\n}\n\nexport async function updateInvoiceStatus(\n  invoice: string,\n  status: POSInvoice['status']\n) {\n  try {\n    await call.post('ury.ury_pos.api.updatePosInvoiceStatus', {\n      invoice,\n      status,\n    });\n  } catch (error) {\n    console.error('Error updating invoice status:', error);\n    throw new Error('Failed to update invoice status');\n  }\n} \n\nexport async function searchPosInvoice(query: string, status: string) {\n  try {\n    const response = await call.get('ury.ury_pos.api.searchPosInvoice', {\n      query,\n      status,\n    });\n    return response.message;\n  } catch (error) {\n    console.error('Error searching POS invoices:', error);\n    throw error;\n  }\n} \n\nexport async function getInvoicePrintHtml(invoiceId: string, printFormat: string) {\n  try {\n    const response = await call.get<{ message: { html: string } }>(\n      'frappe.www.printview.get_html_and_style',\n      {\n        doc: 'POS Invoice',\n        name: invoiceId,\n        print_format: printFormat,\n        _lang: 'en',\n        no_letterhead: 1,\n        letterhead:\"No Letterhead\",\n        settings:{}\n      }\n    );\n    return response.message.html;\n  } catch (error) {\n    console.error('Error fetching invoice print HTML:', error);\n    throw new Error('Failed to fetch invoice print HTML');\n  }\n} \n\nexport async function networkPrint(orderId: string, printer: string, printFormat: string) {\n  await call.post('ury.ury.api.ury_print.network_printing', {\n    doctype: 'POS Invoice',\n    name: orderId,\n    printer_setting: printer,\n    print_format: printFormat,\n  });\n}\n\nexport async function selectNetworkPrinter(orderId: string, posProfile: string, printFormat?: string | null) {\n  await call.post('ury.ury.api.ury_print.select_network_printer', {\n    invoice_id: orderId,\n    pos_profile: posProfile,\n    print_format: printFormat,\n  });\n}\n\n\nexport async function updatePrintStatus(orderId: string) {\n  await call.post('ury.ury.api.ury_print.qz_print_update', { invoice: orderId });\n} "
  },
  {
    "path": "pos/src/lib/menu-api.ts",
    "content": "import { call } from './frappe-sdk';\n\nexport interface MenuItem {\n  item: string;\n  item_name: string;\n  item_image: string | null;\n  rate: number | string;\n  course: string;\n  course_label?: string;\n  trending?: boolean;\n  popular?: boolean;\n  recommended?: boolean;\n  description?: string;\n  special_dish?: 1 | 0;\n}\n\nexport interface GetMenuResponse {\n  message: {\n    items: MenuItem[];\n  };\n}\n\nexport interface GetAggregatorMenuResponse {\n  message: MenuItem[];\n}\n\nexport const getRestaurantMenu = async (posProfile: string, room: string | null, order_type: string | null) => {\n  try {\n    const response = await call.get<GetMenuResponse>(\n      'ury.ury_pos.api.getRestaurantMenu',\n      {\n        pos_profile: posProfile,\n        room: room,\n        order_type: order_type\n      }\n    );\n    return response.message.items;\n  } catch (error: any) {\n    if (error._server_messages) {\n      const messages = JSON.parse(error._server_messages);\n      const message = JSON.parse(messages[0]);\n      throw new Error(message.message);\n    }\n    throw error;\n  }\n};\n\nexport const getAggregatorMenu = async (aggregator: string) => {\n  try {\n    const response = await call.get<GetAggregatorMenuResponse>(\n      'ury.ury_pos.api.getAggregatorItem',\n      {\n        aggregator\n      }\n    );\n    return response.message;\n  } catch (error: any) {\n    if (error._server_messages) {\n      const messages = JSON.parse(error._server_messages);\n      const message = JSON.parse(messages[0]);\n      throw new Error(message.message);\n    }\n    throw error;\n  }\n}; "
  },
  {
    "path": "pos/src/lib/menu-course-api.ts",
    "content": "import { call } from './frappe-sdk';\n\nexport interface MenuCourse {\n  name: string;\n  label: string;\n}\n\nexport interface MenuCourseResponse {\n  message: MenuCourse[];\n}\n\n\nexport async function getMenuCourses(): Promise<MenuCourse[]> {\n  const response = await call.get<MenuCourseResponse>(\n    'ury.ury_pos.api.getMenuCourses'\n  );\n  return response.message;\n}"
  },
  {
    "path": "pos/src/lib/order-api.ts",
    "content": "import { call } from './frappe-sdk';\n\nexport interface POSInvoiceItem {\n  name: string;\n  item_code: string;\n  item_name: string;\n  description: string;\n  item_group: string;\n  image: string;\n  qty: number;\n  comment: string;\n  rate: number;\n  amount: number;\n  discount_percentage: number;\n  discount_amount: number;\n}\n\nexport interface POSInvoice {\n  name: string;\n  title: string;\n  customer: string;\n  customer_name: string;\n  mobile_number: string;\n  customer_group: string;\n  territory: string;\n  posting_date: string;\n  posting_time: string;\n  order_type: string;\n  restaurant_table: string;\n  custom_restaurant_room: string;\n  status: string;\n  total: number;\n  grand_total: number;\n  items: POSInvoiceItem[];\n}\n\nexport interface TableOrder {\n  message: POSInvoice | null;\n}\n\n/**\n * Fetches the current active order/invoice for a table if any exists\n * @param table_no The table number to fetch the order for\n * @returns The order details and customer information if an active order exists\n */\nexport async function getTableOrder(table_no: string): Promise<TableOrder> {\n  const { call } = await import('./frappe-sdk');\n  try {\n    const res = await call.get('ury.ury.doctype.ury_order.ury_order.get_order_invoice', { \n      table: table_no\n    });\n    return res as TableOrder;\n  } catch (error) {\n    console.error('Error fetching table order:', error);\n    return { message: null };\n  }\n} \n\nexport interface SyncOrderRequest {\n  table?: string;\n  customer?: string;\n  items: Array<{\n    item: string;\n    item_name: string;\n    rate: number;\n    qty: number;\n  }>;\n  no_of_pax: number;\n  mode_of_payment?: string;\n  cashier?: string;\n  owner?: string;\n  waiter?: string;\n  pos_profile: string;\n  invoice: string | null;\n  aggregator_id?: string | null;\n  order_type: string;\n  last_invoice: string | null;\n  comments?: string | null;\n  room?: string;\n}\n\nexport const syncOrder = async (data: SyncOrderRequest) => {\n  return call.post( 'ury.ury.doctype.ury_order.ury_order.sync_order',data);\n}; "
  },
  {
    "path": "pos/src/lib/payment-api.ts",
    "content": "import { call } from './frappe-sdk';\n\ninterface PaymentMode {\n  mode_of_payment: string;\n  opening_amount: number;\n}\n\ninterface PaymentModeResponse {\n  message: PaymentMode[];\n}\n\nexport const getPaymentModes = async (): Promise<string[]> => {\n  // Check session storage first\n  const cached = sessionStorage.getItem('payment_modes');\n  if (cached) {\n    return JSON.parse(cached);\n  }\n\n  try {\n    const response = await call.get<PaymentModeResponse>(\"ury.ury_pos.api.getModeOfPayment\");\n\n    const paymentModes = response.message.map((mode:PaymentMode) => mode.mode_of_payment);\n    \n    // Cache in session storage\n    sessionStorage.setItem('payment_modes', JSON.stringify(paymentModes));\n    \n    return paymentModes;\n  } catch (error) {\n    console.error('Failed to fetch payment modes:', error);\n    throw error;\n  }\n}; "
  },
  {
    "path": "pos/src/lib/pos-opening-api.ts",
    "content": "import { call } from './frappe-sdk';\n\nexport interface POSOpeningResponse {\n  message: number;\n}\n\nexport interface POSCloseValidationResponse {\n  message: string;\n}\n\nexport const checkPOSOpening = async (): Promise<POSOpeningResponse> => {\n  try {\n    const response = await call.get<POSOpeningResponse>(\n      'ury.ury_pos.api.posOpening'\n    );\n    \n    return response;\n  } catch (error) {\n    console.error('Error checking POS opening status:', error);\n    throw error;\n  }\n};\n\nexport const validatePOSClose = async (posProfile: string): Promise<POSCloseValidationResponse> => {\n  try {\n    const response = await call.get<POSCloseValidationResponse>(\n      'ury.ury_pos.api.validate_pos_close',\n      {\n        pos_profile: posProfile\n      }\n    );\n    \n    return response;\n  } catch (error) {\n    console.error('Error validating POS close status:', error);\n    throw error;\n  }\n}; "
  },
  {
    "path": "pos/src/lib/pos-profile-api.ts",
    "content": "import { DOCTYPES } from '../data/doctypes';\nimport { call, db } from './frappe-sdk';\n\n// Limited fields response\nexport interface PosProfileLimited {\n  pos_profile: string;\n  branch: string;\n  company: string;\n  waiter: string;\n  warehouse: string;\n  cashier: string;\n  print_format: string | null;\n  qz_print: number;\n  qz_host: string | null;\n  printer: string | null;\n  print_type: string;\n  tableAttention: number;\n  paid_limit: number;\n  disable_rounded_total: number;\n  enable_discount: number;\n  multiple_cashier: number;\n  owner: string;\n  edit_order_type?: number;\n}\n\nexport interface PosProfileLimitedResponse {\n  message: PosProfileLimited;\n}\n\ninterface RolePermission {\n  name: string;\n  owner: string;\n  creation: string;\n  modified: string;\n  modified_by: string;\n  docstatus: number;\n  idx: number;\n  role: string;\n  parent: string;\n  parentfield: string;\n  parenttype: string;\n  doctype: string;\n}\n\n// Full POS Profile response\nexport interface PosProfileFull {\n  name: string;\n  owner: string;\n  creation: string;\n  modified: string;\n  modified_by: string;\n  docstatus: number;\n  idx: number;\n  company: string;\n  customer: string | null;\n  country: string;\n  disabled: number;\n  warehouse: string;\n  campaign: string | null;\n  company_address: string | null;\n  restaurant: string;\n  branch: string;\n  currency: string;\n  role_allowed_for_billing: RolePermission[];\n  role_restricted_for_table_order?: RolePermission[];\n  paid_limit?: number;\n}\n\n// Combined POS Profile with both limited and full fields\nexport interface PosProfileCombined extends PosProfileFull {\n  // Add limited fields that don't exist in full profile\n  waiter: string;\n  cashier: string;\n  print_format: string | null;\n  qz_print: number;\n  qz_host: string | null;\n  printer: string | null;\n  print_type: string;\n  tableAttention: number;\n  paid_limit: number;\n  disable_rounded_total: number;\n  enable_discount: number;\n  multiple_cashier: number;\n  edit_order_type?: number;\n  view_all_status?: number;\n  custom_daily_pos_close?: number;\n}\n\nexport interface Currency {\n  name: string;\n  symbol: string;\n  fraction: string;\n  fraction_units: number;\n  smallest_currency_fraction_value: number;\n  number_format: string;\n}\n\nexport interface PosProfileFullResponse {\n  message: PosProfileFull;\n}\n\nexport async function getPosProfileLimitedFields(): Promise<PosProfileLimited> {\n  const res = await call.get('ury.ury_pos.api.getPosProfile');\n  return res.message;\n}\n\nexport async function getPosProfileFull(posProfileName: string): Promise<PosProfileFull> {\n  const doc = await db.getDoc(DOCTYPES.POS_PROFILE, posProfileName);\n  return doc;\n}\n\nexport async function getCombinedPosProfile(): Promise<PosProfileCombined> {\n  // Get limited fields first\n  const limitedProfile = await getPosProfileLimitedFields();\n  console.log('limitedProfile', limitedProfile);\n  \n  // Get full profile using the pos_profile name from limited profile\n  const fullProfile = await getPosProfileFull(limitedProfile.pos_profile);\n  \n  // Merge both profiles\n  const combinedProfile: PosProfileCombined = {\n    ...fullProfile,\n    waiter: limitedProfile.waiter,\n    cashier: limitedProfile.cashier,\n    print_format: limitedProfile.print_format,\n    qz_print: limitedProfile.qz_print,\n    qz_host: limitedProfile.qz_host,\n    printer: limitedProfile.printer,\n    print_type: limitedProfile.print_type,\n    tableAttention: limitedProfile.tableAttention,\n    paid_limit: limitedProfile.paid_limit,\n    disable_rounded_total: limitedProfile.disable_rounded_total,\n    enable_discount: limitedProfile.enable_discount,\n    multiple_cashier: limitedProfile.multiple_cashier,\n    edit_order_type: limitedProfile.edit_order_type,\n  };\n\n  return combinedProfile;\n}\n\nexport async function getCurrencyInfo(currencyCode: string): Promise<Currency> {\n  const doc = await db.getDoc(DOCTYPES.CURRENCY, currencyCode);\n  return doc;\n}"
  },
  {
    "path": "pos/src/lib/print-qz.ts",
    "content": "import qz from 'qz-tray';\nimport axios from 'axios';\nimport { privateKey } from '../../privateKey';\nimport { KEYUTIL, KJUR, stob64, hextorstr } from 'jsrsasign';\n\nexport async function loadQzPrinter(host: string): Promise<void> {\n  qz.security.setCertificatePromise((resolve: (data: string) => void, reject: (err?: string) => void) => {\n    axios.get('/assets/ury/files/cert.pem')\n      .then(({ data }) => resolve(data))\n      .catch((err) => reject('Error fetching certificate: ' + String(err)));\n  });\n  if (!qz.websocket.isActive()) {\n    await qz.websocket.connect({ host, usingSecure: false });\n  }\n}\n\nexport function disconnectQzPrinter(): void {\n  if (qz.websocket.isActive()) qz.websocket.disconnect();\n}\n\nexport async function printWithQz(host: string, htmlToPrint: string): Promise<void> {\n  qz.security.setSignatureAlgorithm('SHA512');\n  qz.security.setSignaturePromise((toSign: string) => (resolve: (sig: string) => void, reject: (err?: string) => void) => {\n    try {\n      // @ts-expect-error: privateKey must be provided securely\n      const pk = KEYUTIL.getKey(privateKey);\n      const sig = new KJUR.crypto.Signature({ alg: 'SHA512withRSA' });\n      sig.init(pk);\n      sig.updateString(toSign);\n      const hex = sig.sign();\n      resolve(stob64(hextorstr(hex)));\n    } catch (err) {\n      reject(String(err));\n    }\n  });\n\n  const printing = async () => {\n    const printer = await qz.printers.getDefault();\n    const data = [{ type: 'html', format: 'plain', data: htmlToPrint }];\n    const config = qz.configs.create(printer);\n    await qz.print(config, data as any);\n  };\n\n  if (qz.websocket.isActive()) {\n    await printing();\n  } else {\n    await loadQzPrinter(host);\n    await printing();\n  }\n} "
  },
  {
    "path": "pos/src/lib/print.ts",
    "content": "import { printWithQz } from './print-qz';\nimport {\n  getInvoicePrintHtml,\n  networkPrint,\n  selectNetworkPrinter,\n  updatePrintStatus\n} from './invoice-api';\nimport { PosProfileCombined } from './pos-profile-api';\n\ninterface PrintOrderParams {\n  orderId: string;\n  posProfile: PosProfileCombined\n}\n\nexport async function printOrder({ orderId, posProfile }: PrintOrderParams): Promise<'qz' | 'network' | 'socket'> {\n  const { print_type, qz_host, print_format, printer, name, cashier, multiple_cashier } = posProfile;\n\n  if (print_type === 'qz') {\n    if (!qz_host) {\n      throw new Error('QZ host is not set');\n    }\n    const html = await getInvoicePrintHtml(orderId, print_format as string);\n    await printWithQz(qz_host, html);\n    await updatePrintStatus(orderId);\n    return 'qz';\n  } else if (print_type === 'network') {\n    if (cashier && !multiple_cashier) {\n      await networkPrint(orderId, printer as string, print_format as string);\n    } else {\n      await selectNetworkPrinter(orderId, name, print_format);\n    }\n    await updatePrintStatus(orderId);\n    return 'network';\n  } else {\n    // Redirect to printview page\n    const url = `/printview?doctype=POS Invoice&name=${orderId}&format=${print_format}&no_letterhead=1&settings={}&letterhead=No Letterhead&trigger_print=1&_lang=en`;\n    window.open(url, '_blank', 'noopener,noreferrer');\n    await updatePrintStatus(orderId);\n    return 'socket';\n  }\n}"
  },
  {
    "path": "pos/src/lib/role-utils.ts",
    "content": "import type { PosProfileCombined } from './pos-profile-api';\nimport type { User } from '../store/slices/auth-slice';\n\nexport const isUserRestrictedFromTableOrders = (\n  user: User | null,\n  posProfile: PosProfileCombined | null\n): boolean => {\n  if (!user || !posProfile || !user.roles || !posProfile.role_restricted_for_table_order) {\n    return false;\n  }\n\n  // Get the restricted roles from the POS profile\n  const restrictedRoles = posProfile.role_restricted_for_table_order.map(role => role.role);\n  \n  // Check if the user has any of the restricted roles\n  const hasRestrictedRole = user.roles.some(role => restrictedRoles.includes(role));\n  \n  return hasRestrictedRole;\n}; "
  },
  {
    "path": "pos/src/lib/storage.ts",
    "content": "export const storage = {\n  savePosProfileFull: (profile: unknown) => {\n    localStorage.setItem('pos_profile', JSON.stringify(profile));\n  },\n\n  getPosProfileFull: () => {\n    const profile = localStorage.getItem('pos_profile');\n    return profile ? JSON.parse(profile) : null;\n  },\n\n  setItem: (key: string, value: string) => {\n    localStorage.setItem(key, value);\n  },\n\n  getItem: (key: string): string | null => {\n    return localStorage.getItem(key);\n  },\n\n  removeItem: (key: string) => {\n    localStorage.removeItem(key);\n  }\n}; "
  },
  {
    "path": "pos/src/lib/table-api.ts",
    "content": "import { DOCTYPES } from '../data/doctypes';\nimport { db } from './frappe-sdk';\n\nexport interface Room {\n  name: string;\n  branch: string;\n}\n\nexport interface Table {\n  name: string;\n  occupied: number;\n  latest_invoice_time: string | null;\n  is_take_away: number;\n  restaurant_room: string;\n  table_shape: 'Circle' | 'Square' | 'Rectangle';\n  no_of_seats?: number;\n  layout_x?: number;\n  layout_y?: number;\n  minimum_seating?: number;\n}\n\n\nexport async function getRestaurantMenu(posProfile: string, room?: string | null) {\n  const { call } = await import('./frappe-sdk');\n  const params: Record<string, string> = { pos_profile: posProfile };\n  if (room) {\n    params.room = room;\n  }\n  const res = await call.get('ury.ury_pos.api.getRestaurantMenu', params);\n  return res.message;\n}\n\nexport async function getRooms(branch: string): Promise<Room[]> {\n  const rooms = await db.getDocList(DOCTYPES.URY_ROOM, {\n    fields: ['name', 'branch'],\n    filters: [['branch', 'like', branch]],\n    limit: \"*\" as unknown as number,\n    asDict: true,\n  });\n  return rooms as Room[];\n}\n\nexport async function getTableCount(room: string, branch?: string): Promise<number> {\n  const filters = [\n    ['restaurant_room', '=', room],\n    ...(branch ? [['branch', '=', branch]] : []),\n  ];\n  const rows = await db.getDocList(DOCTYPES.URY_TABLE, {\n    fields: ['count(name) as count'],\n    filters: filters as any,\n    limit: 1,\n    asDict: true,\n  }) as Array<{ count?: number | string }>;\n  const countValue = rows[0]?.count ?? 0;\n  return typeof countValue === 'number' ? countValue : Number(countValue) || 0;\n}\nexport async function getTables(room: string): Promise<Table[]> {\n  const tables = await db.getDocList(DOCTYPES.URY_TABLE, {\n    fields: [\n      'name',\n      'occupied',\n      'latest_invoice_time',\n      'is_take_away',\n      'restaurant_room',\n      'table_shape',\n      'no_of_seats',\n      'layout_x',\n      'layout_y',\n      'minimum_seating'\n    ],\n    filters: [['restaurant_room', '=', room]],\n    asDict: true,\n  });\n\n  return tables as Table[];\n}\n\n\nexport async function updateTableLayout(name: string, data: Partial<Table>) {\n  return db.updateDoc(DOCTYPES.URY_TABLE, name, data);\n}\n\n"
  },
  {
    "path": "pos/src/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\nimport { storage } from './storage';\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nexport function formatCurrency(amount: number): string {\n  const symbol = storage.getItem('currencySymbol');\n  return `${symbol} ${amount}`;\n} \n\nexport const formatInvoiceTime = (timestamp: string | null) => {\n    if (!timestamp) return 'No bill activity yet';\n\n    const parsedDate = new Date(timestamp);\n    if (!Number.isNaN(parsedDate.getTime())) {\n      return parsedDate.toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric' });\n    }\n\n    const timeOnlyMatch = timestamp.match(/^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?$/);\n    if (timeOnlyMatch) {\n      const [, hours, minutes, seconds] = timeOnlyMatch;\n      const date = new Date();\n      date.setHours(Number(hours), Number(minutes), Number(seconds), 0);\n      const formatted = date.toLocaleTimeString(undefined, {\n        hour: '2-digit',\n        minute: '2-digit',\n        hour12: false,\n      });\n      if (/^\\d{1,2}:\\d{2}$/.test(formatted)) {\n        return formatted;\n      }\n      return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;\n    }\n\n    return timestamp;\n  };"
  },
  {
    "path": "pos/src/main.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport './index.css'\nimport App from './App.tsx'\nimport { initI18n } from './i18n'\n\ninitI18n().then(() => {\n  createRoot(document.getElementById('root')!).render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n})\n\n  "
  },
  {
    "path": "pos/src/pages/Orders.tsx",
    "content": "import React, { useEffect, useRef } from 'react';\nimport { Clock, User, UserCheck, Receipt, Printer, Pencil, X } from 'lucide-react';\nimport { Badge, Button, Card, CardContent } from '../components/ui';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '../components/ui/dialog';\nimport { showToast } from '../components/ui/toast';\nimport OrderStatusSidebar from '../components/OrderStatusSidebar';\nimport { useRootStore } from '../store/root-store';\nimport { formatCurrency } from '../lib/utils';\nimport { Spinner } from '../components/ui/spinner';\nimport { Textarea } from '../components/ui/textarea';\nimport { usePOSStore } from '../store/pos-store';\nimport { useNavigate } from 'react-router-dom';\nimport PaymentDialog from '../components/PaymentDialog';\nimport { printOrder } from '../lib/print';\nimport { call } from '../lib/frappe-sdk';\nimport { t } from '../i18n';\n\nexport default function Orders() {\n  const { \n    orders,\n    orderLoading,\n    error,\n    selectedStatus,\n    pagination,\n    selectedOrder,\n    selectedOrderItems,\n    selectedOrderTaxes,\n    selectedOrderLoading,\n    selectedOrderError,\n    fetchOrders,\n    setSelectedStatus,\n    goToNextPage,\n    goToPreviousPage,\n    selectOrder,\n    clearSelectedOrder,\n    orderSearchQuery\n  } = useRootStore();\n\n  const posStore = usePOSStore();\n  const navigate = useNavigate();\n  const mounted = useRef(false);\n  const [cancelDialogOpen, setCancelDialogOpen] = React.useState(false);\n  const [cancelReason, setCancelReason] = React.useState('');\n  const [cancelLoading, setCancelLoading] = React.useState(false);\n  const [editLoading, setEditLoading] = React.useState(false);\n  const [showPaymentDialog, setShowPaymentDialog] = React.useState(false);\n  const [isPrinting, setIsPrinting] = React.useState(false);\n\n  useEffect(() => {\n    fetchOrders();\n  }, [fetchOrders]);\n\n  useEffect(() => {\n    if (!mounted.current) {\n      mounted.current = true;\n      return; // Skip the first run\n    }\n    fetchOrders();\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [orderSearchQuery]);\n\n\n  // Function to format the date and time\n  const formatDateTime = (date: string, time: string) => {\n    const formattedDate = new Date(date + ' ' + time).toLocaleString('en-US', {\n      month: 'short',\n      day: 'numeric',\n      hour: 'numeric',\n      minute: 'numeric',\n      hour12: true\n    });\n    return formattedDate;\n  };\n\n  const handleOrderClick = (order: any) => {\n    selectOrder(order);\n  };\n\n  // Helper function to get badge variant based on order status\n  const getBadgeVariant = (status: string) => {\n    switch (status) {\n      case 'Draft':\n      case 'Unbilled':\n        return 'secondary';\n      case 'Recently Paid':\n      case 'Paid':\n      case 'Consolidated':\n        return 'default';\n      case 'Return':\n        return 'destructive';\n      default:\n        return 'default';\n    }\n  };\n\n  async function handleCancelOrder() {\n    if (!selectedOrder) return;\n    if (!cancelReason.trim()) {\n      showToast.error(t('errors.enter_cancel_reason'));\n      return;\n    }\n    setCancelLoading(true);\n    try {\n      await call.post('ury.ury.doctype.ury_order.ury_order.cancel_order', {\n        invoice_id: selectedOrder.name,\n        reason: cancelReason\n      })\n      showToast.success(t('success.order_cancelled'));\n      setCancelDialogOpen(false);\n      setCancelReason('');\n      clearSelectedOrder();\n      fetchOrders();\n    } catch (err) {\n      showToast.error(err instanceof Error ? err.message : t('errors.failed_cancel_order'));\n    } finally {\n      setCancelLoading(false);\n    }\n  }\n\n  async function handleEditOrder() {\n    if (!selectedOrder) return;\n    setEditLoading(true);\n    try {\n      const res = await fetch(`/api/method/frappe.client.get?doctype=POS+Invoice&name=${selectedOrder.name}`);\n      if (!res.ok) throw new Error('Failed to fetch order details');\n      const data = await res.json();\n      const order = data.message;\n      // Fill POS store\n      posStore.resetOrderState();\n      posStore.setSelectedOrderType(order.order_type);\n      posStore.setOrderForUpdate(order.name);\n      if (order.restaurant_table) {\n        posStore.setSelectedTable(order.restaurant_table, order.custom_restaurant_room || null,true);\n      }\n      posStore.setSelectedCustomer({ id: order.customer, name: order.customer_name, phone: order.mobile_number });\n      // Fill cart\n      const items = (order.items || []).map((item: any) => ({\n        id: item.item_code,\n        name: item.item_name,\n        price: item.rate,\n        quantity: item.qty,\n        amount: item.amount,\n        image: item.image || null,\n        uniqueId: item.name,\n        item: item.item_code,\n        item_name: item.item_name,\n        item_image: null,\n        course: '',\n        description: item.description || '',\n        special_dish: 0,\n        tax_rate: 0,\n      }));\n      for (const cartItem of items) {\n        await posStore.addToOrder(cartItem);\n      }\n      // Redirect to POS page\n      navigate('/');\n    } catch (err) {\n      showToast.error(err instanceof Error ? err.message : t('errors.failed_edit_order'));\n    } finally {\n      setEditLoading(false);\n    }\n  }\n\n  async function handlePrintOrder() {\n    if (!selectedOrder || !posStore.posProfile) return;\n    setIsPrinting(true);\n    try {\n      await printOrder({\n        orderId: selectedOrder.name,\n        posProfile: posStore.posProfile\n      });\n      showToast.success(t('success.printed'));\n      // Locally update selectedOrder.invoice_printed to 1\n      if (selectedOrder && typeof selectedOrder === 'object') {\n        selectOrder({ ...selectedOrder, invoice_printed: 1 });\n      }\n      // If order was Unbilled, set to Draft and reload draft orders\n      if (selectedStatus === 'Unbilled') {\n        showToast.info(t('success.order_moved_to_draft'));\n        setSelectedStatus('Draft');\n        fetchOrders();\n      }\n    } catch (err: any) {\n      showToast.error(t('errors.print_failed', { reason: err?.message || String(err) }));\n    } finally {\n      setIsPrinting(false);\n    }\n  }\n\n  if (error) {\n    return (\n      <div className=\"flex items-center justify-center h-screen\">\n        <div className=\"text-center\">\n          <p className=\"text-xl font-semibold text-red-600 mb-2\">Failed to load orders</p>\n          <p className=\"text-gray-600\">{error}</p>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex h-screen overflow-hidden\">\n      {/* Left Sidebar - Order Types */}\n      <OrderStatusSidebar\n        selectedStatus={selectedStatus}\n        setSelectedStatus={setSelectedStatus}\n      />\n\n      {/* Middle Section - Order Cards */}\n      <div className=\"flex-1 flex flex-col h-screen overflow-hidden pe-96\">\n        <div className=\"flex-1 overflow-y-auto bg-gray-50 p-4 pb-40\">\n          {orderLoading ? (\n            <div className=\"flex items-center justify-center h-full\">\n              <Spinner />\n            </div>\n          ) : orders.length === 0 ? (\n            <div className=\"text-center mt-10\">\n              <p className=\"text-gray-500\">{t('orders.no_orders_found')}</p>\n            </div>\n          ) : (\n            <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 max-w-screen-xl mx-auto\">\n              {orders.map((order) => (\n                <Card \n                  key={order.name} \n                  className={`p-0 bg-white hover:shadow-md transition-shadow flex flex-col overflow-hidden cursor-pointer ${\n                    selectedOrder?.name === order.name ? 'ring-2 ring-blue-500 shadow-lg' : ''\n                  }`}\n                  onClick={() => handleOrderClick(order)}\n                >\n                  <CardContent className=\"p-0 flex flex-col h-full\">\n                    <div className=\"p-3 bg-gray-50 border-b\">\n                    <h3 className=\"font-medium text-gray-900 text-sm truncate\" title={order.name}>\n                      {order.name}\n                    </h3>\n                    <div className=\"flex items-center justify-between\">\n                      <div>\n                        <p className=\"text-xs text-gray-500\">\n                          {order.restaurant_table ? `Table ${order.restaurant_table} • ` : ''}{t(`order_types.${order.order_type.toLowerCase().replace(/ /g, '_')}`)}\n                        </p>\n                      </div>\n                      <Badge variant={getBadgeVariant(order.status)} className=\"ms-2\">\n                        {t(`order_status_types.${order.status.toLowerCase().replace(/ /g, '_')}`)}\n                      </Badge>\n                    </div>\n                    </div>\n\n                    {/* Content section - matches MenuCard padding and structure */}\n                    <div className=\"flex-1 p-3 flex flex-col\">\n                      <div className=\"\">\n                        <p className=\"text-sm text-gray-900\">{order.customer}</p>\n                      </div>\n\n                      <div className=\"flex items-center gap-2 text-xs text-gray-500\">\n                        <Clock className=\"w-3.5 h-3.5\" />\n                        <span>{formatDateTime(order.posting_date, order.posting_time)}</span>\n                      </div>\n\n                      {/* Total - pushed to bottom like MenuCard */}\n                      <div className=\"mt-auto pt-2\">\n                        <span className=\"text-sm font-semibold text-gray-900 tabular-nums\">\n                          {formatCurrency(order.rounded_total)}\n                        </span>\n                      </div>\n                    </div>\n                  </CardContent>\n                </Card>\n              ))}\n            </div>\n          )}\n          {/* Pagination Controls */}\n          {!orderLoading && (\n            <div className=\"py-4\">\n              <div className=\"flex justify-center items-center gap-x-4 max-w-screen-xl mx-auto\">\n                <Button\n                  onClick={goToPreviousPage}\n                  disabled={pagination.currentPage === 1}\n                  variant=\"outline\"\n                  className='w-20'\n                  size=\"xs\"\n                >\n                  {t('orders.pagination.previous')}\n                </Button>\n                <div className=\"flex items-center gap-2\">\n                  <span className=\"text-sm text-muted-foreground\">\n                    {t('orders.pagination.page', { number: pagination.currentPage.toString() })}\n                  </span>\n                </div>\n                <Button\n                  onClick={goToNextPage}\n                  disabled={!pagination.hasNextPage}\n                  variant=\"outline\"\n                  className='w-20'\n                  size=\"xs\"\n                >\n                  {t('orders.pagination.next')}\n                </Button>\n              </div>\n            </div>\n          )}\n        </div>\n      </div>\n\n      {/* Right Section - Order Details */}\n      <div className=\"w-96 bg-white border-s border-gray-200 flex flex-col h-[calc(100vh-4rem)] fixed end-0 z-10\">\n        {!selectedOrder ? (\n          <div className=\"text-center h-full flex flex-col items-center justify-center text-gray-500 p-6\">\n            <p className=\"text-lg font-medium mb-2\">{t('order.select_to_view')}</p>\n            <p className=\"text-sm\">{t('orders.click_to_view')}</p>\n          </div>\n        ) : selectedOrderLoading ? (\n          <div className=\"flex items-center justify-center h-full\">\n            <Spinner />\n          </div>\n        ) : selectedOrderError ? (\n          <div className=\"text-center h-full flex flex-col items-center justify-center text-red-500 p-6\">\n            <p className=\"text-lg font-medium mb-2\">Failed to load order details</p>\n            <p className=\"text-sm\">{selectedOrderError}</p>\n          </div>\n        ) : (\n          <>\n            {/* Fixed Header */}\n            <div className=\"sticky top-0 start-0 end-0 z-20 bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between min-h-[64px]\">\n              <h2 className=\"text-xl font-semibold text-gray-900 truncate max-w-[10rem]\">{selectedOrder.name}</h2>\n              <div className=\"flex items-center gap-2\">\n                {/* Only show edit and cancel buttons for Draft, Unbilled, and Recently Paid orders */}\n                {(selectedOrder.status === 'Draft' || selectedOrder.status === 'Unbilled' || selectedOrder.status === 'Recently Paid') && (\n                  <>\n                    <button\n                      type=\"button\"\n                      className=\"inline-flex items-center justify-center rounded-md p-2 bg-gray-100 hover:bg-gray-200 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500\"\n                      aria-label=\"Edit order\"\n                      onClick={handleEditOrder}\n                      disabled={editLoading}\n                    >\n                      <Pencil className=\"w-4 h-4\" />\n                      {editLoading && <span className=\"ms-2 text-xs\">{t('common.loading')}</span>}\n                    </button>\n                    <button\n                      type=\"button\"\n                      className=\"inline-flex items-center justify-center rounded-md p-2 bg-gray-100 hover:bg-gray-200 text-red-600 focus:outline-none focus:ring-2 focus:ring-red-500\"\n                      aria-label=\"Cancel order\"\n                      onClick={() => setCancelDialogOpen(true)}\n                    >\n                      <X className=\"w-4 h-4\" />\n                    </button>\n                  </>\n                )}\n                <Badge variant={getBadgeVariant(selectedOrder.status)}>\n                  {t(`order_status_types.${selectedOrder.status.toLowerCase().replace(/ /g, '_')}`)}\n                </Badge>\n              </div>\n            </div>\n            {/* Cancel Order Dialog */}\n            <Dialog open={cancelDialogOpen} onOpenChange={setCancelDialogOpen}>\n              <DialogContent>\n                <DialogHeader>\n                  <DialogTitle>{t('order.cancel_order')}</DialogTitle>\n                  <DialogDescription>\n                    {t('errors.enter_cancel_reason')}\n                  </DialogDescription>\n                </DialogHeader>\n                <div className=\"px-6 mb-3\">\n                <Textarea\n                  placeholder={t('order.enter_cancel_reason')}\n                  value={cancelReason}\n                  onChange={e => setCancelReason(e.target.value)}\n                  disabled={cancelLoading}\n                  autoFocus\n                />\n                </div>\n                <DialogFooter>\n                  <Button variant=\"outline\" onClick={() => setCancelDialogOpen(false)} disabled={cancelLoading}>\n                    {t('common.cancel')}\n                  </Button>\n                  <Button variant=\"danger\" onClick={handleCancelOrder} disabled={cancelLoading}>\n                    {cancelLoading ? t('common.cancelling') : t('common.confirm_cancel')}\n                  </Button>\n                </DialogFooter>\n              </DialogContent>\n            </Dialog>\n            {/* Scrollable Content Area */}\n            <div className=\"flex-1 overflow-y-auto p-6 pb-40\">\n              {/* Order Header (now only info, not name/buttons) */}\n              <div className=\"mb-6\">\n                {/* Two-column Order Info */}\n                <div className=\"grid grid-cols-2 gap-4\">\n                  {/* First column: customer and time */}\n                  <div className=\"space-y-3\">\n                    <div className=\"flex items-center gap-3 text-sm\">\n                      <User className=\"w-4 h-4 text-gray-500\" />\n                      <span className=\"text-gray-900 font-medium\">{selectedOrder.customer}</span>\n                    </div>\n                    <div className=\"flex items-center gap-3 text-sm\">\n                      <Clock className=\"w-4 h-4 text-gray-500\" />\n                      <span className=\"text-gray-600\">{formatDateTime(selectedOrder.posting_date, selectedOrder.posting_time)}</span>\n                    </div>\n                  </div>\n                  {/* Second column: waiter and table */}\n                  <div className=\"space-y-3\">\n                    <div className=\"flex items-center gap-3 text-sm\">\n                      <UserCheck className=\"w-4 h-4 text-gray-500\" />\n                      <span className=\"text-gray-600\">{selectedOrder.waiter}</span>\n                    </div>\n                    {selectedOrder.restaurant_table && (\n                      <div className=\"flex items-center gap-3 text-sm\">\n                        <Receipt className=\"w-4 h-4 text-gray-500\" />\n                        <span className=\"text-gray-600\">{selectedOrder.restaurant_table}</span>\n                      </div>\n                    )}\n                  </div>\n                </div>\n              </div>\n\n              {/* Order Items */}\n              <div className=\"mb-6\">\n                <h3 className=\"text-lg font-semibold text-gray-900 mb-4\">{t('order.items_title')}</h3>\n                <div className=\"space-y-3\">\n                  {selectedOrderItems.map((item, index) => (\n                    <div key={index} className=\"flex justify-between items-start py-2 border-b border-gray-100\">\n                      <div className=\"flex-1\">\n                        <p className=\"text-sm font-medium text-gray-900\">{item.item_name}</p>\n                        <p className=\"text-xs text-gray-500\">Qty: {item.qty}</p>\n                      </div>\n                      <div className=\"text-right\">\n                        <p className=\"text-sm font-semibold text-gray-900\">\n                          {formatCurrency(item.amount)}\n                        </p>\n                      </div>\n                    </div>\n                  ))}\n                </div>\n              </div>\n\n              {/* Taxes */}\n              {selectedOrderTaxes.length > 0 && (\n                <div className=\"mb-6\">\n                  <h3 className=\"text-lg font-semibold text-gray-900 mb-4\">{t('order.taxes_charges')}</h3>\n                  <div className=\"space-y-2\">\n                    {selectedOrderTaxes.map((tax, index) => (\n                      <div key={index} className=\"flex justify-between items-center py-1\">\n                        <span className=\"text-sm text-gray-600\">{tax.description}</span>\n                        <span className=\"text-sm font-medium text-gray-900\">\n                          {formatCurrency(tax.rate)}\n                        </span>\n                      </div>\n                    ))}\n                  </div>\n                </div>\n              )}\n            </div>\n\n            {/* Sticky Bottom Section - Single Row: Print | Payment | Total */}\n            <div className=\"border-t border-gray-200 p-6 bg-gray-50 sticky bottom-0 start-0 end-0 z-10\">\n              <div className=\"flex items-center gap-3 w-full\">\n                {/* Print Icon Button */}\n                <Button\n                  variant=\"outline\"\n                  size=\"icon\"\n                  className=\"flex-shrink-0\"\n                  onClick={handlePrintOrder}\n                  aria-label=\"Print\"\n                  disabled={isPrinting}\n                >\n                  {isPrinting ? <Spinner className=\"w-5 h-5\" hideMessage /> : <Printer className=\"w-5 h-5\" />}\n                </Button>\n                {/* Payment Button - Only show for Draft, Unbilled, and Recently Paid orders */}\n                {(selectedOrder.status === 'Draft' || selectedOrder.status === 'Unbilled' || selectedOrder.status === 'Recently Paid') && (\n                  <Button\n                    className=\"flex-1\"\n                    onClick={() => {\n                      if (String(selectedOrder.invoice_printed) === '0') {\n                        showToast.error(t('errors.please_print_first'));\n                        return;\n                      }\n                      setShowPaymentDialog(true);\n                    }}\n                  >\n                    {t('order.payment')}\n                  </Button>\n                )}\n                {/* Total */}\n                <span className=\"ms-auto text-xl font-bold text-gray-900 whitespace-nowrap\">\n                  {formatCurrency(selectedOrder.rounded_total)}\n                </span>\n              </div>\n            </div>\n          </>\n        )}\n      </div>\n      {showPaymentDialog && selectedOrder && (\n        <PaymentDialog\n          onClose={() => setShowPaymentDialog(false)}\n          grandTotal={selectedOrder.grand_total}\n          roundedTotal={selectedOrder.rounded_total}\n          invoice={selectedOrder.name}\n          customer={selectedOrder.customer}\n          posProfile={posStore.posProfile?.name || ''}\n          table={selectedOrder.restaurant_table || null}\n          cashier={posStore.posProfile?.cashier || ''}\n          owner={posStore.posProfile?.cashier || ''}\n          fetchOrders={fetchOrders}\n          clearSelectedOrder={clearSelectedOrder}\n        />\n      )}\n    </div>\n  );\n};\n"
  },
  {
    "path": "pos/src/pages/POS.tsx",
    "content": "import React, { useState, useRef, useEffect } from 'react';\nimport { t } from '../i18n';\nimport { Star, TrendingUp } from 'lucide-react';\nimport Sidebar from '../components/Sidebar';\nimport OrderPanel from '../components/OrderPanel';\nimport ProductDialog from '../components/ProductDialog';\nimport MenuList from '../components/MenuList';\nimport SearchBar from '../components/SearchBar';\nimport { usePOSStore } from '../store/pos-store';\nimport { cn } from '../lib/utils';\nimport { Spinner } from '../components/ui/spinner';\nimport InitialLoader from '../components/InitialLoader';\n\nexport default function POS() {\n  const {\n    searchQuery,\n    setSearchQuery,\n    quickFilter,\n    setQuickFilter,\n    setSelectedItem,\n    addToOrder,\n    loading,\n    error,\n    isMenuInteractionDisabled,\n    isInitializing,\n  } = usePOSStore();\n  \n  const [isDialogOpen, setIsDialogOpen] = useState(false);\n  const [showSearch, setShowSearch] = useState(false);\n  const clickTimerRef = useRef<NodeJS.Timeout | null>(null);\n  const clickCountRef = useRef(0);\n\n  useEffect(() => {\n    if (showSearch) {\n      // The searchInputRef.current.focus() line was removed as per the new_code,\n      // as the SearchBar component now handles its own focus.\n    }\n  }, [showSearch]);\n\n  const handleItemClick = (item: any) => {\n    if (isMenuInteractionDisabled()) return;\n    \n    clickCountRef.current += 1;\n    \n    if (clickTimerRef.current) {\n      clearTimeout(clickTimerRef.current);\n    }\n\n    clickTimerRef.current = setTimeout(() => {\n      if (clickCountRef.current === 1) {\n        // Single click - add to cart\n        addToOrder({ ...item, quantity: 1 });\n      } else if (clickCountRef.current === 2) {\n        // Double click - open dialog\n        setSelectedItem(item);\n        setIsDialogOpen(true);\n      }\n      clickCountRef.current = 0;\n    }, 250); // 250ms threshold for double click\n  };\n\n  const QuickFilterButton = ({ filter, icon: Icon, label }: { \n    filter: 'all' | 'special';\n    icon: React.ElementType;\n    label: string;\n  }) => (\n    <button\n      onClick={() => setQuickFilter(filter)}\n      className={cn(\n        'flex items-center gap-2 px-3 py-1.5 rounded-full text-sm font-medium transition-colors',\n        quickFilter === filter\n          ? 'bg-blue-100 text-blue-700'\n          : 'bg-gray-100 text-gray-700 hover:bg-gray-200',\n        isMenuInteractionDisabled() && 'opacity-50 cursor-not-allowed pointer-events-none'\n      )}\n      disabled={isMenuInteractionDisabled()}\n    >\n      <Icon className=\"w-4 h-4\" />\n      {label}\n    </button>\n  );\n\n  if (isInitializing) {\n    return <InitialLoader />;\n  }\n\n  if (error) {\n    return (\n      <div className=\"flex items-center justify-center h-screen\">\n        <div className=\"text-center\">\n          <p className=\"text-xl font-semibold text-red-600 mb-2\">Failed to load POS</p>\n          <p className=\"text-gray-600\">{error}</p>\n          <button \n            onClick={() => window.location.reload()}\n            className=\"mt-4 px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700\"\n          >\n            Retry\n          </button>\n        </div>\n      </div>\n    );\n  }\n\n  if (loading) {\n    return (\n      <div className=\"flex-1 flex items-center justify-center\">\n        <Spinner message={t('common.loading_menu_items')} />\n      </div>\n    );\n  }\n\n  if (error) {\n    return (\n      <div className=\"flex-1 flex items-center justify-center\">\n        <div className=\"text-center\">\n          <p className=\"text-lg font-medium text-red-600\">{t('common.error_loading_menu_items')}</p>\n          <p className=\"text-sm text-gray-500 mt-2\">{error}</p>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex flex-1 overflow-hidden\">\n      <Sidebar disabled={isMenuInteractionDisabled()} />\n      <div className=\"flex-1 flex flex-col h-screen overflow-hidden pe-96\">\n        <div className=\"p-4 bg-white border-b border-gray-200\">\n          <div className=\"max-w-screen-xl mx-auto space-y-3\">\n            <div className=\"flex items-center gap-2 overflow-x-auto overflow-y-hidden\">\n              {/* <SearchBar\n                value={searchQuery}\n                onChange={setSearchQuery}\n                onVisibilityChange={setShowSearch}\n                isVisible={showSearch}\n                disabled={isMenuInteractionDisabled()}\n              /> */}\n              \n              <QuickFilterButton filter=\"all\" icon={Star} label={t('common.all')} />\n              <QuickFilterButton filter=\"special\" icon={TrendingUp} label={t('menu.special_items')} />\n            </div>\n          </div>\n        </div>\n\n        <MenuList onItemClick={handleItemClick} />\n      </div>\n      <OrderPanel />\n      {isDialogOpen && <ProductDialog onClose={() => setIsDialogOpen(false)} />}\n    </div>\n  );\n}\n"
  },
  {
    "path": "pos/src/pages/Table.tsx",
    "content": "import { useCallback, useEffect, useMemo, useState, type MouseEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { AlertTriangle, Eye, Layout, Loader2, Printer, Square, Users } from 'lucide-react';\nimport { cn, formatInvoiceTime } from '../lib/utils';\nimport { usePOSStore } from '../store/pos-store';\nimport { getRooms, getTables, getTableCount ,type Room, type Table } from '../lib/table-api';\nimport { Spinner } from '../components/ui/spinner';\nimport { Button } from '../components/ui/button';\nimport { Badge } from '../components/ui/badge';\nimport { DINE_IN } from '../data/order-types';\nimport { TableShapeIcon } from '../components/TableShapeIcon';\nimport { getTableOrder } from '../lib/order-api';\nimport { printOrder } from '../lib/print';\nimport { showToast } from '../components/ui/toast';\nimport { t } from '../i18n';\n\nimport LayoutView from '../components/LayoutView';\n\nconst sortTables = (tables: Table[]) => [...tables].sort((a, b) => a.name.localeCompare(b.name));\n\nconst TableView = () => {\n  const navigate = useNavigate();\n  const { posProfile, setSelectedTable, setSelectedOrderType } = usePOSStore();\n\n  const branch = posProfile?.branch ?? null;\n  const [rooms, setRooms] = useState<Room[]>([]);\n  const [selectedRoom, setSelectedRoom] = useState<string | null>(null);\n  const [tables, setTables] = useState<Table[]>([]);\n  const [tablesCache, setTablesCache] = useState<Record<string, Table[]>>({});\n  const [loadingRooms, setLoadingRooms] = useState(false);\n  const [loadingTables, setLoadingTables] = useState(false);\n  const [roomCounts, setRoomCounts] = useState<Record<string, number>>({});\n  const [loadingRoomCounts, setLoadingRoomCounts] = useState(false);\n  const [error, setError] = useState<string | null>(null);\n  const [printingTable, setPrintingTable] = useState<string | null>(null);\n\n  const persistRoomCounts = useCallback((counts: Record<string, number>) => {\n    if (!branch) return;\n    sessionStorage.setItem(`ury_room_counts_${branch}`, JSON.stringify(counts));\n  }, [branch]);\n\n  useEffect(() => {\n    async function fetchRooms() {\n      if (!branch) return;\n      setLoadingRooms(true);\n      setError(null);\n\n      try {\n        const sessionKey = `ury_rooms_${branch}`;\n        const cachedRooms = sessionStorage.getItem(sessionKey);\n\n        if (cachedRooms) {\n          const parsedRooms = JSON.parse(cachedRooms) as Room[];\n          setRooms(parsedRooms);\n          setSelectedRoom(prev => prev ?? (parsedRooms[0]?.name ?? null));\n        } else {\n          const fetchedRooms = await getRooms(branch);\n          setRooms(fetchedRooms);\n          setSelectedRoom(prev => prev ?? (fetchedRooms[0]?.name ?? null));\n          sessionStorage.setItem(sessionKey, JSON.stringify(fetchedRooms));\n        }\n      } catch (e) {\n        console.error(e);\n        setError('Failed to load rooms');\n      } finally {\n        setLoadingRooms(false);\n      }\n    }\n\n    fetchRooms();\n  }, [branch]);\n\n  useEffect(() => {\n    if (!branch || rooms.length === 0) return;\n    const cacheKey = `ury_room_counts_${branch}`;\n    const cachedCounts = sessionStorage.getItem(cacheKey);\n    let shouldFetch = true;\n\n    if (cachedCounts) {\n      try {\n        const parsedCounts = JSON.parse(cachedCounts) as Record<string, number>;\n        setRoomCounts(parsedCounts);\n        const hasAllRooms = rooms.every(room => typeof parsedCounts[room.name] === 'number');\n        if (hasAllRooms) {\n          shouldFetch = false;\n        }\n      } catch {\n        sessionStorage.removeItem(cacheKey);\n      }\n    }\n\n    if (!shouldFetch) return;\n\n  async function fetchRoomCounts() {\n      setLoadingRoomCounts(true);\n      try {\n        const counts = await Promise.all(\n          rooms.map(room => getTableCount(room.name, room.branch))\n        );\n        const nextCounts = rooms.reduce((acc, room, index) => {\n          acc[room.name] = counts[index];\n          return acc;\n        }, {} as Record<string, number>);\n        setRoomCounts(nextCounts);\n        persistRoomCounts(nextCounts);\n      } catch (error) {\n        console.error('Failed to load room counts', error);\n      } finally {\n        setLoadingRoomCounts(false);\n      }\n    }\n\n    fetchRoomCounts();\n  }, [branch, rooms, persistRoomCounts]);\n\n  const loadTables = useCallback(\n    async (roomName: string, options?: { useCache?: boolean }) => {\n      if (!roomName) return;\n      setError(null);\n\n      const shouldUseCache = options?.useCache !== false;\n      if (shouldUseCache && tablesCache[roomName]) {\n        setTables(sortTables(tablesCache[roomName]));\n        setLoadingTables(false);\n        return;\n      }\n\n      setLoadingTables(true);\n      try {\n        const fetchedTables = await getTables(roomName);\n        const sortedTables = sortTables(fetchedTables);\n        setTables(sortedTables);\n        setTablesCache(prev => ({ ...prev, [roomName]: sortedTables }));\n      } catch (e) {\n        console.error(e);\n        setError('Failed to load tables');\n        setTables([]);\n      } finally {\n        setLoadingTables(false);\n      }\n    },\n    [tablesCache]\n  );\n\n  useEffect(() => {\n    if (!selectedRoom) return;\n    loadTables(selectedRoom);\n  }, [selectedRoom, loadTables]);\n\n  const handleNavigateToPOS = (tableName: string) => {\n    if (!selectedRoom) return;\n    setSelectedOrderType(DINE_IN);\n    setSelectedTable(tableName, selectedRoom);\n    navigate('/');\n  };\n\n  const handlePreviewTable = (table: Table, event?: MouseEvent<HTMLButtonElement>) => {\n    event?.stopPropagation();\n    handleNavigateToPOS(table.name);\n  };\n\n  const handlePrintTable = async (table: Table, event: MouseEvent<HTMLButtonElement>) => {\n    event.stopPropagation();\n\n    if (!posProfile) {\n      showToast.error('POS profile not loaded yet');\n      return;\n    }\n\n    setPrintingTable(table.name);\n    try {\n      const orderResponse = await getTableOrder(table.name);\n      const invoiceId = orderResponse.message?.name;\n\n      if (!invoiceId) {\n        showToast.error('No active order found for this table');\n        return;\n      }\n\n      await printOrder({ orderId: invoiceId, posProfile });\n      showToast.success('Printed successfully');\n      await loadTables(table.restaurant_room, { useCache: false });\n    } catch (error) {\n      showToast.error(error instanceof Error ? error.message : 'Failed to print order');\n    } finally {\n      setPrintingTable(null);\n    }\n  };\n\n  const tablesToDisplay = useMemo(() => sortTables(tables), [tables]);\n\n  const hasRooms = rooms.length > 0;\n  const showGridSkeleton = loadingTables || !selectedRoom;\n\n  const handleRoomChange = (roomName: string) => {\n    if (roomName === selectedRoom) {\n      loadTables(roomName, { useCache: false });\n      return;\n    }\n\n    setSelectedRoom(roomName);\n\n    if (tablesCache[roomName]) {\n      setTables(sortTables(tablesCache[roomName]));\n      setLoadingTables(false);\n    } else {\n      setLoadingTables(true);\n      setTables([]);\n    }\n  };\n\n  const [isLayoutView, setIsLayoutView] = useState(false);\n\n  const handleLayoutView = () => {\n    if (selectedRoom) {\n      loadTables(selectedRoom, { useCache: false });\n    }\n    setIsLayoutView(true);\n  };\n\n  if (isLayoutView && selectedRoom) {\n    return (\n      <LayoutView\n        selectedRoom={selectedRoom}\n        tables={tablesToDisplay}\n        onBackToGrid={() => setIsLayoutView(false)}\n        onRefresh={() => loadTables(selectedRoom, { useCache: false })}\n      />\n    );\n  }\n\n  return (\n    <div className=\"flex flex-col h-full\">\n      <div className=\"p-4 bg-white border-b border-gray-200\">\n        <div className=\"max-w-screen-xl mx-auto\">\n          <div className=\"flex flex-col gap-3\">\n            <div className=\"flex justify-between items-start gap-4\">\n              <div className=\"flex flex-wrap gap-2\">\n                {loadingRooms && (\n                  <div className=\"flex-1 min-w-[160px]\">\n                    <Spinner message=\"Loading rooms...\" />\n                  </div>\n                )}\n\n                {!loadingRooms && !hasRooms && (\n                  <div className=\"flex items-center gap-2 text-gray-500 text-sm\">\n                    <AlertTriangle className=\"w-4 h-4\" />\n                    No rooms found for this branch\n                  </div>\n                )}\n\n                {rooms.map(room => (\n                  <Button\n                    key={room.name}\n                    variant=\"tab\"\n                    data-selected={selectedRoom === room.name}\n                    onClick={() => handleRoomChange(room.name)}\n                    className=\"h-fit\"\n                  >\n                    {room.name}\n                    {typeof roomCounts[room.name] === 'number' ? (\n                      <Badge variant=\"outline\" className=\"ml-2 bg-white/60\">\n                        {roomCounts[room.name]}\n                      </Badge>\n                    ) : null}\n                  </Button>\n                ))}\n              </div>\n\n              <div className=\"flex-shrink-0\">\n                <Button\n                  variant=\"tab\"\n                  className=\"flex items-center gap-2 text-sm\"\n                  onClick={() => handleLayoutView()}\n                >\n                  <Layout className=\"w-4 h-4\" />\n                  {t('tables.layout_view')}\n                </Button>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div className=\"flex-1 overflow-auto bg-gray-50 p-6\">\n        <div className=\"max-w-screen-xl mx-auto h-full\">\n          {error && !loadingTables ? (\n            <div className=\"h-full flex flex-col items-center justify-center gap-3 text-red-500\">\n              <AlertTriangle className=\"w-10 h-10\" />\n              <p>{error}</p>\n            </div>\n          ) : showGridSkeleton ? (\n            <Spinner message={t('common.loading_tables')} />\n          ) : tablesToDisplay.length === 0 ? (\n            <div className=\"h-full flex flex-col items-center justify-center gap-3 text-gray-500\">\n              <Square className=\"w-10 h-10\" />\n              <p>{t('tables.no_tables_found')}</p>\n            </div>\n          ) : (\n            <div className=\"grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\">\n              {tablesToDisplay.map(table => {\n                const isOccupied = table.occupied === 1;\n\n                return (\n                  <div\n                    key={table.name}\n                    role={isOccupied ? 'group' : 'button'}\n                    tabIndex={isOccupied ? -1 : 0}\n                    onClick={() => {\n                      if (!isOccupied) {\n                        handleNavigateToPOS(table.name);\n                      }\n                    }}\n                    className={cn(\n                      'relative bg-white rounded-lg border-2 p-4 transition-all flex flex-col justify-between gap-y-4',\n                      isOccupied\n                        ? 'border-amber-400 bg-amber-50 text-amber-900'\n                        : 'border-emerald-300 bg-emerald-50 text-emerald-900 hover:border-emerald-400 hover:shadow-md cursor-pointer',\n                    )}\n                  >\n                    <div>\n                      <div className=\"flex items-center justify-between mb-3\">\n                        <div className=\"flex items-center gap-2\">\n                          <TableShapeIcon shape={table.table_shape || 'Rectangle'} />\n                          <span className=\"font-semibold text-lg text-gray-900\">{table.name}</span>\n                        </div>\n                        <Badge variant={isOccupied ? 'warning' : 'success'}>\n                          {isOccupied ? t('tables.occupied') : t('tables.available')}\n                        </Badge>\n                      </div>\n\n                      <div className=\"space-y-2 text-sm text-gray-700\">\n                        <div className=\"flex items-center justify-between\">\n                          <span className=\"font-medium\">{t('tables.room')}</span>\n                          <span>{table.restaurant_room}</span>\n                        </div>\n                        {isOccupied && (\n                          <div className=\"flex items-center justify-between\">\n                            <span className=\"font-medium\">Started at</span>\n                            <span>{formatInvoiceTime(table.latest_invoice_time)}</span>\n                          </div>\n                        )}\n                        {typeof table.no_of_seats === 'number' && (\n                          <div className=\"flex items-center justify-between\">\n                            <span className=\"font-medium\">{t('tables.seats')}</span>\n                            <span className=\"flex items-center gap-1\">\n                              <Users className=\"w-3 h-3\" />\n                              {table.no_of_seats}\n                            </span>\n                          </div>\n                        )}\n                        {table.is_take_away === 1 && (\n                          <Badge variant=\"pending\" className=\"mt-2\">\n                            Take away\n                          </Badge>\n                        )}\n                      </div>\n                    </div>\n\n                    {isOccupied ? (\n                      <div className=\"flex gap-2 pt-3 mt-3 border-t border-amber-200\">\n                        <button\n                          onClick={(event) => handlePreviewTable(table, event)}\n                          className=\"flex-1 flex items-center justify-center gap-2 py-2 text-xs font-semibold rounded bg-white hover:bg-amber-100 transition\"\n                        >\n                          <Eye className=\"w-3 h-3\" />\n                          Preview\n                        </button>\n                        <button\n                          onClick={(event) => handlePrintTable(table, event)}\n                          disabled={printingTable === table.name}\n                          className=\"flex-1 flex items-center justify-center gap-2 py-2 text-xs font-semibold rounded bg-white hover:bg-amber-100 transition disabled:opacity-60 disabled:cursor-not-allowed\"\n                        >\n                          {printingTable === table.name ? (\n                            <>\n                              <Loader2 className=\"w-3 h-3 animate-spin\" />\n                              Printing...\n                            </>\n                          ) : (\n                            <>\n                              <Printer className=\"w-3 h-3\" />\n                              Print\n                            </>\n                          )}\n                        </button>\n                      </div>\n                    ) : (\n                      <p className=\"text-sm text-gray-500\">{t('tables.tap_to_start')}</p>\n                    )}\n                  </div>\n                );\n              })}\n            </div>\n          )}\n        </div>\n      </div>\n\n      {/* Status Legend */}\n      <div className=\"fixed bottom-[4.5rem] w-full p-4 bg-white border-t border-gray-200\">\n        <div className=\"max-w-screen-xl mx-auto\">\n          <div className=\"flex items-center justify-center gap-6 text-sm\">\n            <div className=\"flex items-center gap-2\">\n              <div className=\"w-4 h-4 bg-green-100 border border-green-300 rounded\"></div>\n              <span>{t('tables.available')}</span>\n            </div>\n            <div className=\"flex items-center gap-2\">\n              <div className=\"w-4 h-4 bg-red-100 border border-red-300 rounded\"></div>\n              <span>{t('tables.occupied')}</span>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default TableView;"
  },
  {
    "path": "pos/src/store/pos-store.ts",
    "content": "import { create } from 'zustand';\nimport { v4 as uuidv4 } from 'uuid';\nimport { storage } from '../lib/storage';\nimport { getRestaurantMenu, getAggregatorMenu, MenuItem as APIMenuItem } from '../lib/menu-api';\nimport { getCurrencyInfo, PosProfileCombined, getCombinedPosProfile } from '../lib/pos-profile-api';\nimport { getMenuCourses } from '../lib/menu-course-api';\nimport { getCustomerGroups, getCustomerTerritories } from '../lib/customer-api';\nimport { DEFAULT_ORDER_TYPE, OrderType } from '../data/order-types';\nimport { getTableOrder, TableOrder } from '../lib/order-api';\nimport { getPaymentModes } from '../lib/payment-api';\n\n// Constants\nconst MAX_QUANTITY = 99;\nconst MIN_QUANTITY = 0;\nconst ITEMS_PER_PAGE = 10;\n\n// Custom error class for cart operations\nclass CartError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = 'CartError';\n  }\n}\n\n// Extend the API MenuItem to include UI-specific properties\nexport interface MenuItem extends Omit<APIMenuItem, 'rate' | 'item_image'> {\n  id: string;\n  name: string;\n  image: string | null;\n  price: number;\n  quantity?: number;\n  description?: string;\n  special_dish?: 1 | 0;\n  variants?: Array<{ id: string; name: string; price: number }>;\n  addons?: Array<{ id: string; name: string; price: number; category: 'sides' | 'drinks' | 'desserts' }>;\n  selectedVariant?: { id: string; name: string; price: number };\n  selectedAddons?: Array<{ id: string; name: string; price: number }>;\n  uniqueId?: string;\n  tax_rate?: number;\n}\n\nexport interface Customer {\n  id: string;\n  name: string;\n  phone: string;\n}\n\nexport interface OrderItem extends MenuItem {\n  quantity: number;\n  selectedVariant?: { id: string; name: string; price: number };\n  selectedAddons?: { id: string; name: string; price: number }[];\n  uniqueId?: string;\n  comment?: string;\n}\n\nexport interface PaymentMode {\n  id: string;\n  name: string;\n  enabled: boolean;\n}\n\nexport interface Category {\n  name: string;\n  label: string;\n}\n\nexport interface Order {\n  id: string;\n  cartId: string;\n  customerId?: string;\n  paymentModeId: string;\n  paymentMode: string;\n  orderType: OrderType;\n  status: 'pending' | 'paid' | 'preparing' | 'ready' | 'completed' | 'cancelled';\n  totalAmount: number;\n  paidAmount: number;\n  createdAt: string;\n  updatedAt: string;\n}\n\ninterface CartTotals {\n  subtotal: number;\n  tax: number;\n  total: number;\n  itemCount: number;\n}\n\ninterface Aggregator {\n  customer: string;\n}\n\ninterface POSState {\n  menuItems: MenuItem[];\n  categories: Category[];\n  activeOrders: OrderItem[];\n  selectedCategory: string;\n  selectedTable: string | null;\n  selectedRoom: string | null;\n  searchQuery: string;\n  selectedCustomer: Customer | null;\n  selectedOrderType: OrderType;\n  quickFilter: 'all' | 'special';\n  selectedItem: MenuItem | null;\n  cartId: string | null;\n  loading: boolean;\n  menuLoading: boolean;\n  orderLoading: boolean;\n  profileLoading: boolean;\n  error: string | null;\n  paymentModes: string[];\n  orders: Order[];\n  selectedAggregator: Aggregator | null;\n  currency: string;\n  currencySymbol: string | null;\n  isUpdatingOrder: boolean;\n  orderId: string | null;\n  posProfile: PosProfileCombined | null;\n  customerGroups: string[];\n  territories: string[];\n  tableOrder: TableOrder | null;\n  isInitializing: boolean;\n  orderComment: string;\n}\n\ninterface POSStore extends POSState {\n  fetchMenuItems: () => Promise<void>;\n  fetchAggregatorMenu: (aggregator: string) => Promise<void>;\n  fetchCategories: () => Promise<void>;\n  fetchPaymentModes: () => Promise<void>;\n  addToOrder: (item: OrderItem) => Promise<void>;\n  removeFromOrder: (uniqueId: string) => Promise<void>;\n  updateQuantity: (uniqueId: string, quantity: number) => Promise<void>;\n  clearOrder: () => Promise<void>;\n  setSelectedCategory: (category: string) => void;\n  setSearchQuery: (query: string) => void;\n  setSelectedCustomer: (customer: Customer | null) => void;\n  setSelectedTable: (table: string | null, room: string | null, doNotLoadOrder?: boolean) => void;\n  setSelectedOrderType: (type: OrderType) => void;\n  setQuickFilter: (filter: 'all' | 'special') => void;\n  setSelectedItem: (item: MenuItem | null) => void;\n  initializeCart: () => Promise<void>;\n  processPayment: (paymentMode: string, amount: number) => Promise<void>;\n  updateOrderStatus: (orderId: string, status: Order['status']) => Promise<void>;\n  fetchPosProfile: () => Promise<void>;\n  fetchCustomerGroups: () => Promise<void>;\n  fetchTerritories: () => Promise<void>;\n  fetchCurrencySymbol: () => Promise<void>;\n  getCartTotals: () => CartTotals;\n  itemExistsInCart: (uniqueId: string) => boolean;\n  validateQuantity: (quantity: number) => boolean;\n  getItemPrice: (item: OrderItem) => number;\n  getItemQuantityFromCart: (item: MenuItem) => number;\n  loadTableOrder: (table: string) => Promise<void>;\n  clearTableOrder: () => void;\n  isMenuInteractionDisabled: () => boolean;\n  isOrderInteractionDisabled: () => boolean;\n  initializeApp: () => Promise<void>;\n  setOrderForUpdate: (orderId: string | null) => void;\n  resetOrderState: () => void;\n  setSelectedAggregator: (aggregator: Aggregator | null) => void;\n  setOrderComment: (comment: string) => void;\n}\n\nconst generateUniqueId = (item: OrderItem): string => {\n  const variantId = item.selectedVariant?.id || 'default';\n  const addonIds = item.selectedAddons?.map(addon => addon.id).sort().join('-') || 'no-addons';\n  return `${item.id}-${variantId}-${addonIds}`;\n};\n\nconst calculateItemPrice = (item: OrderItem): number => {\n  const basePrice = item.selectedVariant?.price || item.price;\n  const addonsTotal = item.selectedAddons?.reduce((sum, addon) => sum + addon.price, 0) || 0;\n  return basePrice + addonsTotal;\n};\n\nexport const usePOSStore = create<POSStore>((set, get) => ({\n  menuItems: [],\n  categories: [],\n  activeOrders: [],\n  selectedCategory: '',\n  selectedTable: null,\n  selectedRoom: null,\n  searchQuery: '',\n  selectedCustomer: null,\n  selectedOrderType: DEFAULT_ORDER_TYPE as OrderType,\n  quickFilter: \"all\",\n  selectedItem: null,\n  cartId: null,\n  loading: false,\n  menuLoading: false,\n  orderLoading: false,\n  profileLoading: false,\n  error: null,\n  paymentModes: ['Cash'],\n  orders: [],\n  posProfile: null,\n  customerGroups: [],\n  territories: [],\n  selectedAggregator: null,\n  currency: storage.getItem('currency') || 'INR',\n  currencySymbol: storage.getItem('currencySymbol') || null,\n  tableOrder: null,\n  isInitializing: true,\n  isUpdatingOrder: false,\n  orderId: null,\n  orderComment: '',\n\n  initializeApp: async () => {\n    try {\n      set({ isInitializing: true, error: null });\n      \n      const [profileResult, menuResult, categoriesResult, paymentModesResult] = await Promise.allSettled([\n        get().fetchPosProfile(),\n        get().fetchMenuItems(),\n        get().fetchCategories(),\n        get().fetchPaymentModes()\n      ]);\n\n      if (profileResult.status === 'rejected' || \n          menuResult.status === 'rejected' || \n          categoriesResult.status === 'rejected' ||\n          paymentModesResult.status === 'rejected') {\n        set({ \n          error: 'Failed to initialize app. Please refresh the page.',\n          isInitializing: false \n        });\n        return;\n      }\n\n      set({ isInitializing: false });\n    } catch (error) {\n      set({ \n        error: 'Failed to initialize app. Please refresh the page.',\n        isInitializing: false \n      });\n    }\n  },\n\n  fetchPosProfile: async () => {\n    try {\n      const cached = sessionStorage.getItem('posProfile');\n      if (cached) {\n        const profile = JSON.parse(cached);\n        set({ \n          posProfile: profile, \n          profileLoading: false,\n          currency: profile.currency || 'INR'\n        });\n        if (!storage.getItem('currencySymbol')) {\n          await get().fetchCurrencySymbol();\n        }\n        return;\n      }\n\n      set({ profileLoading: true, error: null });\n      const combinedProfile = await getCombinedPosProfile();\n      \n      sessionStorage.setItem('posProfile', JSON.stringify(combinedProfile));\n      set({ \n        posProfile: combinedProfile, \n        profileLoading: false,\n        currency: combinedProfile.currency || 'INR'\n      });\n      \n      if (!storage.getItem('currencySymbol')) {\n        await get().fetchCurrencySymbol();\n      }\n    } catch (error) {\n      console.error('Error fetching POS profile:', error);\n      set({ \n        error: 'Failed to fetch POS profile',\n        profileLoading: false \n      });\n    }\n  },\n\n  fetchCurrencySymbol: async () => {\n    try {\n      const currency = get().currency;\n      const response = await getCurrencyInfo(currency);\n      const { symbol } = response;\n      \n      set({ currencySymbol: symbol });\n      storage.setItem('currencySymbol', symbol);\n    } catch (error) {\n      console.error('Error fetching currency symbol:', error);\n      set({ currencySymbol: get().currency });\n      storage.setItem('currencySymbol', get().currency);\n    }\n  },\n\n  fetchMenuItems: async () => {\n    const { posProfile, selectedRoom, selectedOrderType } = get();\n    if (!posProfile?.restaurant) return;\n\n    try {\n      set({ menuLoading: true, error: null });\n      const items = await getRestaurantMenu(posProfile.name, selectedRoom, selectedOrderType);\n      \n      const menuItems: MenuItem[] = items.map(item => ({\n        id: item.item,\n        name: item.item_name,\n        image: item.item_image || null,\n        price: typeof item.rate === 'string' ? parseFloat(item.rate) : item.rate || 0,\n        item: item.item,\n        item_name: item.item_name,\n        item_image: item.item_image,\n        course: item.course,\n        course_label: item.course_label || item.course,\n        description: item.description || '',\n        special_dish: item.special_dish || 0,\n        tax_rate: 0,\n      }));\n\n      set({ menuItems });\n    } catch (error) {\n      set({ error: 'Failed to load menu items' });\n      console.error('Error loading menu items:', error);\n    } finally {\n      set({ menuLoading: false });\n    }\n  },\n\n  fetchAggregatorMenu: async (aggregator: string) => {\n    try {\n      set({ menuLoading: true, error: null });\n      const items = await getAggregatorMenu(aggregator);\n      \n      const menuItems: MenuItem[] = items.map(item => ({\n        ...item,\n        id: item.item,\n        name: item.item_name,\n        image: item.item_image || null,\n        price: typeof item.rate === 'string' ? parseFloat(item.rate) : item.rate || 0,\n        category: item.course\n      }));\n\n      set({ menuItems, menuLoading: false });\n    } catch (error) {\n      set({ error: 'Failed to load aggregator menu', menuLoading: false });\n      console.error('Error loading aggregator menu:', error);\n    }\n  },\n\n  fetchCategories: async () => {\n    try {\n      const cached = sessionStorage.getItem('menuCategories');\n      if (cached) {\n        const categories = JSON.parse(cached);\n        set({ categories });\n        return;\n      }\n\n      const courses = await getMenuCourses();\n      sessionStorage.setItem('menuCategories', JSON.stringify(courses));\n      set({ categories: courses });\n    } catch (error) {\n      set({ error: 'Failed to load menu categories' });\n      throw error;\n    }\n  },\n\n  fetchPaymentModes: async () => {\n    try {\n      const modes = await getPaymentModes();\n      set({ paymentModes: modes });\n    } catch (error) {\n      console.error('Failed to fetch payment modes:', error);\n    }\n  },\n\n  initializeCart: async () => {\n    set({ cartId: uuidv4() });\n  },\n\n  addToOrder: async (item: OrderItem) => {\n    try {\n      if (!get().validateQuantity(item.quantity)) {\n        throw new CartError(`Quantity must be between ${MIN_QUANTITY} and ${MAX_QUANTITY}`);\n      }\n\n      const uniqueId = generateUniqueId(item);\n      const existingItemIndex = get().activeOrders.findIndex(orderItem => orderItem.uniqueId === uniqueId);\n\n      if (existingItemIndex !== -1) {\n        const existingItem = get().activeOrders[existingItemIndex];\n        const newQuantity = existingItem.quantity + item.quantity;\n        const newComment = item.comment !== undefined ? item.comment : existingItem?.comment || \"\";\n\n        if (!get().validateQuantity(newQuantity)) {\n          throw new CartError(`Cannot add item. Total quantity would exceed ${MAX_QUANTITY}`);\n        }\n\n        const newOrders = [...get().activeOrders];\n        newOrders[existingItemIndex] = {\n          ...existingItem,\n          quantity: newQuantity,\n          comment: newComment\n        };\n        \n        set({ activeOrders: newOrders });\n      } else {\n        const newOrders = [...get().activeOrders, { ...item, uniqueId }];\n        set({ activeOrders: newOrders });\n      }\n    } catch (error) {\n      if (error instanceof CartError) {\n        set({ error: error.message });\n      } else {\n        set({ error: 'Failed to add item to cart' });\n      }\n    }\n  },\n\n  removeFromOrder: async (uniqueId: string) => {\n    try {\n      const newOrders = get().activeOrders.filter(item => item.uniqueId !== uniqueId);\n      set({ activeOrders: newOrders });\n    } catch (error) {\n      set({ error: 'Failed to remove item from cart' });\n    }\n  },\n\n  updateQuantity: async (uniqueId: string, quantity: number) => {\n    try {\n      if (!get().validateQuantity(quantity)) {\n        throw new CartError(`Quantity must be between ${MIN_QUANTITY} and ${MAX_QUANTITY}`);\n      }\n\n      const newOrders = get().activeOrders.map(item =>\n        item.uniqueId === uniqueId ? { ...item, quantity } : item\n      );\n      set({ activeOrders: newOrders });\n    } catch (error) {\n      if (error instanceof CartError) {\n        set({ error: error.message });\n      } else {\n        set({ error: 'Failed to update quantity' });\n      }\n    }\n  },\n\n  clearOrder: async () => {\n    try {\n      set({ activeOrders: [] });\n    } catch (error) {\n      set({ error: 'Failed to clear cart' });\n    }\n  },\n\n  setSelectedCategory: (category) => set({ selectedCategory: category }),\n  setSearchQuery: (query) => set({ searchQuery: query }),\n  setSelectedCustomer: (customer) => set({ selectedCustomer: customer }),\n  setSelectedTable: (table: string | null, room: string | null, doNotLoadOrder: boolean = false) => {\n    set({ selectedTable: table, selectedRoom: room });\n    if (table ) {\n      if (!doNotLoadOrder) \n        get().loadTableOrder(table);\n    } else {\n      get().clearTableOrder();\n    }\n    if (room) {\n      get().fetchMenuItems();\n    }\n  },\n  setSelectedOrderType: (type) => {\n    const { fetchMenuItems } = get();\n    \n    set({ \n      activeOrders: [],\n      selectedOrderType: type,\n      isUpdatingOrder: false,\n      orderId: null\n    });\n    \n    if (type !== 'Aggregators') {\n      fetchMenuItems();\n    }\n  },\n  setQuickFilter: (filter) => set({ quickFilter: filter }),\n  setSelectedItem: (item) => set({ selectedItem: item }),\n  setSelectedAggregator: (aggregator) => set({ selectedAggregator: aggregator }),\n  setOrderComment: (comment: string) => set({ orderComment: comment }),\n\n  processPayment: async (paymentMode: string, amount: number) => {\n    try {\n      const { activeOrders, cartId, selectedCustomer, selectedOrderType } = get();\n      \n      const order: Order = {\n        id: uuidv4(),\n        cartId: cartId!,\n        customerId: selectedCustomer?.id,\n        paymentModeId: paymentMode,\n        paymentMode,\n        orderType: selectedOrderType,\n        status: 'paid',\n        totalAmount: amount,\n        paidAmount: amount,\n        createdAt: new Date().toISOString(),\n        updatedAt: new Date().toISOString()\n      };\n\n      const newOrders = [...get().orders, order];\n      set({ orders: newOrders });\n      \n      await get().clearOrder();\n    } catch (error) {\n      set({ error: (error as Error).message });\n    }\n  },\n\n  updateOrderStatus: async (orderId: string, status: Order['status']) => {\n    try {\n      const newOrders = get().orders.map(order => \n        order.id === orderId \n          ? { ...order, status, updatedAt: new Date().toISOString() }\n          : order\n      );\n      set({ orders: newOrders });\n    } catch (error) {\n      set({ error: (error as Error).message });\n    }\n  },\n\n  fetchCustomerGroups: async () => {\n    const cached = sessionStorage.getItem('customerGroups');\n    if (cached) {\n      set({ customerGroups: JSON.parse(cached) });\n      return;\n    }\n    const groups = await getCustomerGroups();\n    const names = groups.map((g: any) => g.name);\n    set({ customerGroups: names });\n    sessionStorage.setItem('customerGroups', JSON.stringify(names));\n  },\n\n  fetchTerritories: async () => {\n    const cached = sessionStorage.getItem('territories');\n    if (cached) {\n      set({ territories: JSON.parse(cached) });\n      return;\n    }\n    const terrs = await getCustomerTerritories();\n    const names = terrs.map((t: any) => t.name);\n    set({ territories: names });\n    sessionStorage.setItem('territories', JSON.stringify(names));\n  },\n\n  getCartTotals: (): CartTotals => {\n    const items = get().activeOrders;\n    const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);\n    \n    const subtotal = items.reduce((sum, item) => {\n      const itemPrice = calculateItemPrice(item);\n      return sum + (itemPrice * item.quantity);\n    }, 0);\n\n    const tax = items.reduce((sum, item) => {\n      const itemPrice = calculateItemPrice(item);\n      const taxRate = item.tax_rate || 0;\n      return sum + (itemPrice * item.quantity * (taxRate / 100));\n    }, 0);\n\n    return {\n      subtotal,\n      tax,\n      total: subtotal + tax,\n      itemCount\n    };\n  },\n\n  itemExistsInCart: (uniqueId: string): boolean => {\n    return get().activeOrders.some(item => item.uniqueId === uniqueId);\n  },\n\n  validateQuantity: (quantity: number): boolean => {\n    return !isNaN(quantity) && quantity >= MIN_QUANTITY && quantity <= MAX_QUANTITY;\n  },\n\n  getItemPrice: (item: OrderItem): number => {\n    return calculateItemPrice(item);\n  },\n\n  getItemQuantityFromCart: (item: MenuItem): number => {\n    const uniqueId = generateUniqueId(item as OrderItem);\n    const cartItem = get().activeOrders.find(orderItem => orderItem.uniqueId === uniqueId);\n    return cartItem?.quantity || 0;\n  },\n\n  loadTableOrder: async (table: string) => {\n    try {\n      set({ orderLoading: true, error: null });\n      const response = await getTableOrder(table);\n      const order = response.message;\n      if (order && order.name && order.items && order.items.length > 0) {\n        const orderItems: OrderItem[] = order.items.map(item => {\n          const orderItem = {\n            id: item.item_code,\n            name: item.item_name,\n            price: item.rate,\n            quantity: item.qty,\n            amount: item.amount,\n            image: item.image || null,\n            item: item.item_code,\n            item_name: item.item_name,\n            item_image: null,\n            course: '',\n            description: item.description || '',\n            special_dish: 0 as 0 | 1,\n            tax_rate: 0,\n          };\n          return {\n            ...orderItem,\n            uniqueId: generateUniqueId(orderItem as OrderItem)\n          } as OrderItem;\n        });\n\n        set({ \n          tableOrder: response,\n          activeOrders: orderItems,\n          selectedCustomer: order.customer ? {\n            id: order.customer,\n            name: order.customer_name,\n            phone: order.mobile_number,\n          } : null,\n          isUpdatingOrder: true,\n          orderId: order.name,\n        });\n      } else {\n        set({ \n          tableOrder: null,\n          activeOrders: [],\n          selectedCustomer: null,\n          isUpdatingOrder: false,\n          orderId: null,\n        });\n      }\n    } catch (error) {\n      set({ \n        error: 'Failed to load table order',\n        tableOrder: null,\n        activeOrders: [],\n        selectedCustomer: null,\n        isUpdatingOrder: false,\n        orderId: null,\n      });\n    } finally {\n      set({ orderLoading: false });\n    }\n  },\n\n  clearTableOrder: () => {\n    set({ \n      tableOrder: null,\n      activeOrders: [],\n      selectedCustomer: null,\n      isUpdatingOrder: false,\n      orderId: null,\n    });\n  },\n\n  setOrderForUpdate: (orderId: string | null) => {\n    set({ \n      isUpdatingOrder: orderId !== null,\n      orderId,\n    });\n  },\n\n  resetOrderState: () => {\n    const { fetchMenuItems } = get();\n    \n    set({\n      selectedCustomer: null,\n      selectedTable: null,\n      selectedRoom: null,\n      selectedAggregator: null,\n      isUpdatingOrder: false,\n      orderId: null,\n      activeOrders: [],\n      selectedItem: null,\n      orderLoading: false,\n      menuItems: [],\n      error: null,\n      selectedOrderType: DEFAULT_ORDER_TYPE,\n      orderComment: '',\n    });\n\n    fetchMenuItems();\n  },\n\n  isMenuInteractionDisabled: () => {\n    const state = get();\n    return state.menuLoading || state.profileLoading;\n  },\n\n  isOrderInteractionDisabled: () => {\n    const state = get();\n    return state.orderLoading;\n  }\n})); "
  },
  {
    "path": "pos/src/store/root-store.ts",
    "content": "import { create } from 'zustand';\nimport { createAuthSlice, AuthSlice } from './slices/auth-slice';\nimport { createConfigSlice, ConfigSlice } from './slices/config-slice';\nimport { createOrdersSlice, OrdersSlice } from './slices/orders-slice';\n\nexport type RootState = AuthSlice & ConfigSlice & OrdersSlice;\n\nexport const useRootStore = create<RootState>()((...args) => ({\n  ...createAuthSlice(...args),\n  ...createConfigSlice(...args),\n  ...createOrdersSlice(...args),\n})); "
  },
  {
    "path": "pos/src/store/slices/auth-slice.ts",
    "content": "import { StateCreator } from 'zustand';\nimport { getLoggedUser, getUserRoles } from '../../lib/auth-api';\n\nexport interface User {\n  name: string; // This stores the user ID\n  roles: string[];\n  full_name?: string;\n}\n\nexport interface AuthState {\n  user: User | null;\n  isLoading: boolean;\n  error: string | null;\n}\n\nexport interface AuthActions {\n  checkAuth: () => Promise<void>;\n  setUser: (user: User | null) => void;\n  clearAuth: () => void;\n}\n\nexport type AuthSlice = AuthState & AuthActions;\n\nconst initialState: AuthState = {\n  user: null,\n  isLoading: false,\n  error: null,\n};\n\nexport const createAuthSlice: StateCreator<AuthSlice> = (set, get) => ({\n  ...initialState,\n\n  checkAuth: async () => {\n    try {\n      set({ isLoading: true, error: null });\n      const response = await getLoggedUser();\n      \n      if (!response) {\n        // If no user is logged in, redirect to login\n        window.location.href = '/login?redirect-to=%2Fpos';\n        return;\n      }\n\n      // Get user roles\n      const roles = await getUserRoles(response);\n\n      set({\n        user: {\n          name: response, // Store the user ID in name field\n          full_name: roles.full_name,\n          roles: roles.roles,\n        },\n        isLoading: false,\n      });\n    } catch (error) {\n      set({ \n        error: (error as Error).message,\n        isLoading: false,\n        user: null,\n      });\n      // Redirect to login on error\n      window.location.href = '/login?redirect-to=%2Fapp';\n    }\n  },\n\n  setUser: (user) => {\n    set({ user });\n  },\n\n  clearAuth: () => {\n    set(initialState);\n  },\n}); "
  },
  {
    "path": "pos/src/store/slices/config-slice.ts",
    "content": "import { StateCreator } from 'zustand';\nimport { AuthSlice } from './auth-slice';\nimport { getCombinedPosProfile, PosProfileCombined } from '../../lib/pos-profile-api';\n\ninterface RolePermission {\n  name: string;\n  owner: string;\n  creation: string;\n  modified: string;\n  modified_by: string;\n  docstatus: number;\n  idx: number;\n  role: string;\n  parent: string;\n  parentfield: string;\n  parenttype: string;\n  doctype: string;\n}\n\nexport interface ConfigState {\n  allowedRoles: string[];\n  isLoading: boolean;\n  error: string | null;\n  hasAccess: boolean;\n  posProfile: PosProfileCombined | null;\n}\n\nexport interface ConfigActions {\n  checkAccess: () => void;\n  setAllowedRoles: (roles: string[]) => void;\n  fetchPosProfile: (forceRefresh?: boolean) => Promise<void>;\n}\n\nexport type ConfigSlice = ConfigState & ConfigActions;\n\nconst initialState: ConfigState = {\n  allowedRoles: [],\n  isLoading: false,\n  error: null,\n  hasAccess: false,\n  posProfile: null,\n};\n\nexport const createConfigSlice: StateCreator<\n  ConfigSlice & AuthSlice,\n  [],\n  [],\n  ConfigSlice\n> = (set, get) => ({\n  ...initialState,\n\n  fetchPosProfile: async (forceRefresh = false) => {\n    try {\n      set({ isLoading: true, error: null });\n\n      // Check session storage first if not forcing refresh\n      const cached = sessionStorage.getItem('posProfile');\n      if (cached && !forceRefresh) {\n        const profile = JSON.parse(cached);\n        set({ posProfile: profile });\n        // Extract and set allowed roles from the profile\n        const allowedRoles = profile.role_allowed_for_billing?.map((role: RolePermission) => role.role) || [];\n        console.log(\"allowedRoles\", allowedRoles);\n        get().setAllowedRoles(allowedRoles);\n        set({ isLoading: false });\n        return;\n      }\n\n      // If not in cache or forcing refresh, fetch from API\n      const profile = await getCombinedPosProfile();\n      \n      // Cache the profile\n      sessionStorage.setItem('posProfile', JSON.stringify(profile));\n      set({ posProfile: profile });\n\n      // Extract and set allowed roles from the profile\n      const allowedRoles = profile.role_allowed_for_billing?.map((role: RolePermission) => role.role) || [];\n      get().setAllowedRoles(allowedRoles);\n      set({ isLoading: false });\n    } catch (error) {\n      set({ \n        error: (error as Error).message,\n        isLoading: false,\n      });\n    }\n  },\n\n  checkAccess: () => {\n    const { user } = get();\n    const { allowedRoles } = get();\n\n    if (!user || !user.roles || !allowedRoles.length) {\n      set({ hasAccess: false });\n      return;\n    }\n\n    // Check if user has any of the allowed roles\n    const hasAccess = user.roles.some(role => allowedRoles.includes(role));\n    set({ hasAccess });\n\n    // If no access, we could redirect or show an error message\n    if (!hasAccess) {\n      set({ error: 'You do not have permission to access this application.' });\n    }\n  },\n\n  setAllowedRoles: (roles) => {\n    set({ allowedRoles: roles });\n    // After setting new roles, recheck access\n    get().checkAccess();\n  },\n}); "
  },
  {
    "path": "pos/src/store/slices/orders-slice.ts",
    "content": "import { StateCreator } from 'zustand';\nimport { OrderType } from '../../data/order-types';\nimport { call } from '../../lib/frappe-sdk';\nimport { getPOSInvoices, getPOSInvoiceItems, POSInvoiceItem, POSInvoiceTax } from '../../lib/invoice-api';\nimport { searchPosInvoice } from '../../lib/invoice-api';\n\nexport interface POSInvoice {\n  name: string;\n  invoice_printed: number;\n  grand_total: number;\n  restaurant_table: string | null;\n  cashier: string;\n  waiter: string;\n  net_total: number;\n  posting_time: string;\n  total_taxes_and_charges: number;\n  customer: string;\n  status: 'Draft' | 'Unbilled' | 'Recently Paid' | 'Paid' | 'Consolidated' | 'Return';\n  mobile_number: string;\n  posting_date: string;\n  rounded_total: number;\n  order_type: OrderType;\n}\n\nexport interface OrdersState {\n  orders: POSInvoice[];\n  orderLoading: boolean;\n  error: string | null;\n  pagination: {\n    currentPage: number;\n    hasNextPage: boolean;\n    itemsPerPage: number;\n  };\n  selectedStatus: 'Draft' | 'Unbilled' | 'Recently Paid' | 'Paid' | 'Consolidated' | 'Return';\n  selectedOrder: POSInvoice | null;\n  selectedOrderItems: POSInvoiceItem[];\n  selectedOrderTaxes: POSInvoiceTax[];\n  selectedOrderLoading: boolean;\n  selectedOrderError: string | null;\n  orderSearchQuery: string;\n}\n\nexport interface OrdersActions {\n  fetchOrders: (page?: number) => Promise<void>;\n  updateOrderStatus: (orderId: string, status: POSInvoice['status']) => Promise<void>;\n  goToNextPage: () => Promise<void>;\n  goToPreviousPage: () => Promise<void>;\n  setSelectedStatus: (status: POSInvoice['status']) => Promise<void>;\n  selectOrder: (order: POSInvoice) => Promise<void>;\n  clearSelectedOrder: () => void;\n  setOrderSearchQuery: (query: string) => void;\n}\n\nexport type OrdersSlice = OrdersState & OrdersActions;\n\nconst ITEMS_PER_PAGE = 10;\n\nexport const createOrdersSlice: StateCreator<\n  OrdersSlice,\n  [],\n  [],\n  OrdersSlice\n> = (set, get) => ({\n  // Initial state\n  orders: [],\n  orderLoading: false,\n  error: null,\n  pagination: {\n    currentPage: 1,\n    hasNextPage: false,\n    itemsPerPage: ITEMS_PER_PAGE,\n  },\n  selectedStatus: 'Draft',\n  selectedOrder: null,\n  selectedOrderItems: [],\n  selectedOrderTaxes: [],\n  selectedOrderLoading: false,\n  selectedOrderError: null,\n  orderSearchQuery: '',\n\n  // Actions\n  fetchOrders: async (page = 1) => {\n    try {\n      set({ orderLoading: true, error: null });\n      const { orderSearchQuery, selectedStatus } = get();\n      \n      // Get POS profile to access paid_limit\n      const posProfile = sessionStorage.getItem('posProfile');\n      const profile = posProfile ? JSON.parse(posProfile) : null;\n      const paidLimit = profile?.paid_limit;\n      \n      if (orderSearchQuery && orderSearchQuery.trim()) {\n        // Use search API\n        const res = await searchPosInvoice(orderSearchQuery, selectedStatus);\n        set({\n          orders: res.data || [],\n          pagination: {\n            currentPage: 1,\n            hasNextPage: false,\n            itemsPerPage: ITEMS_PER_PAGE,\n          },\n          orderLoading: false\n        });\n        return;\n      }\n      // Default fetch\n      const limitStart = (page - 1) * ITEMS_PER_PAGE;\n      const status = selectedStatus;\n      const { invoices, hasMore } = await getPOSInvoices({\n        status,\n        limit: ITEMS_PER_PAGE,\n        limit_start: limitStart,\n        paid_limit: paidLimit\n      });\n      set({ \n        orders: invoices,\n        pagination: {\n          currentPage: page,\n          hasNextPage: hasMore,\n          itemsPerPage: ITEMS_PER_PAGE,\n        },\n        orderLoading: false \n      });\n    } catch (error) {\n      set({ \n        error: error instanceof Error ? error.message : 'Failed to fetch orders',\n        orderLoading: false \n      });\n    }\n  },\n\n  goToNextPage: async () => {\n    const { pagination, orderLoading } = get();\n    if (!orderLoading && pagination.hasNextPage) {\n      await get().fetchOrders(pagination.currentPage + 1);\n    }\n  },\n\n  goToPreviousPage: async () => {\n    const { pagination, orderLoading } = get();\n    if (!orderLoading && pagination.currentPage > 1) {\n      await get().fetchOrders(pagination.currentPage - 1);\n    }\n  },\n\n  setSelectedStatus: async (status) => {\n    set({ selectedStatus: status });\n    // Clear selected order when status changes\n    get().clearSelectedOrder();\n    await get().fetchOrders(1); // Reset to first page when status changes\n  },\n\n  selectOrder: async (order) => {\n    try {\n      set({ \n        selectedOrder: order,\n        selectedOrderLoading: true, \n        selectedOrderError: null \n      });\n\n      const { items, taxes } = await getPOSInvoiceItems(order.name);\n      \n      set({ \n        selectedOrderItems: items,\n        selectedOrderTaxes: taxes,\n        selectedOrderLoading: false \n      });\n    } catch (error) {\n      set({ \n        selectedOrderError: error instanceof Error ? error.message : 'Failed to fetch order details',\n        selectedOrderLoading: false \n      });\n    }\n  },\n\n  clearSelectedOrder: () => {\n    set({ \n      selectedOrder: null,\n      selectedOrderItems: [],\n      selectedOrderTaxes: [],\n      selectedOrderError: null \n    });\n  },\n\n  updateOrderStatus: async (orderId: string, status: POSInvoice['status']) => {\n    try {\n      set({ orderLoading: true, error: null });\n\n      await call.post('ury.ury_pos.api.updatePosInvoiceStatus', {\n        invoice: orderId,\n        status,\n      });\n\n      // Refresh the orders list after status update\n      await get().fetchOrders(get().pagination.currentPage);\n      \n      set({ orderLoading: false });\n    } catch (error) {\n      set({ \n        error: error instanceof Error ? error.message : 'Failed to update order status',\n        orderLoading: false \n      });\n    }\n  },\n\n  setOrderSearchQuery: (query) => set({ orderSearchQuery: query }),\n}); "
  },
  {
    "path": "pos/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "pos/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\n    \"./index.html\",\n    \"./src/**/*.{js,ts,jsx,tsx}\",\n  ],\n  theme: {\n    extend: {\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n          50: \"hsl(var(--primary-50))\",\n          100: \"hsl(var(--primary-100))\",\n          200: \"hsl(var(--primary-200))\",\n          300: \"hsl(var(--primary-300))\",\n          400: \"hsl(var(--primary-400))\",\n          500: \"hsl(var(--primary-500))\",\n          600: \"hsl(var(--primary-600))\",\n          700: \"hsl(var(--primary-700))\",\n          800: \"hsl(var(--primary-800))\",\n          900: \"hsl(var(--primary-900))\",\n          950: \"hsl(var(--primary-950))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n          50: \"hsl(var(--accent-50))\",\n          100: \"hsl(var(--accent-100))\",\n          200: \"hsl(var(--accent-200))\",\n          300: \"hsl(var(--accent-300))\",\n          400: \"hsl(var(--accent-400))\",\n          500: \"hsl(var(--accent-500))\",\n          600: \"hsl(var(--accent-600))\",\n          700: \"hsl(var(--accent-700))\",\n          800: \"hsl(var(--accent-800))\",\n          900: \"hsl(var(--accent-900))\",\n          950: \"hsl(var(--accent-950))\",\n        },\n        gray: {\n          50: \"hsl(var(--gray-50))\",\n          100: \"hsl(var(--gray-100))\",\n          200: \"hsl(var(--gray-200))\",\n          300: \"hsl(var(--gray-300))\",\n          400: \"hsl(var(--gray-400))\",\n          500: \"hsl(var(--gray-500))\",\n          600: \"hsl(var(--gray-600))\",\n          700: \"hsl(var(--gray-700))\",\n          800: \"hsl(var(--gray-800))\",\n          900: \"hsl(var(--gray-900))\",\n          950: \"hsl(var(--gray-950))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n        white: \"hsl(var(--white))\",\n        black: \"hsl(var(--black))\",\n      },\n      fontFamily: {\n        inter: ['Inter', 'sans-serif'],\n      },\n      borderRadius: {\n        lg: \"var(--radius)\",\n        md: \"calc(var(--radius) - 2px)\",\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n      spacing: {\n        'order-panel': 'var(--order-panel-width)',\n        'badge-min': 'var(--badge-min-width)',\n        'dialog-max-w': 'var(--dialog-max-width)',\n        'dialog-max-h': 'var(--dialog-max-height)',\n      },\n    },\n  },\n  plugins: [],\n} "
  },
  {
    "path": "pos/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUncheckedSideEffectImports\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "pos/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "pos/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"node\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUncheckedSideEffectImports\": true,\n    \"types\": [\"node\"]\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "pos/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { fileURLToPath } from 'url'\nimport { dirname, resolve } from 'path'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n// https://vite.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n  resolve: {\n    alias: {\n      \"@\": resolve(__dirname, \"./src\"),\n    },\n  },\n  build: {\n    outDir: \"../ury/public/pos\",\n    emptyOutDir: true,\n  },\n})\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"ury\"\nauthors = [\n    { name = \"Tridz Technologies\", email = \"info@tridz.com\"}\n]\ndescription = \"A Complete Restaurant Order Taking Software\"\nrequires-python = \">=3.10\"\nreadme = \"README.md\"\ndynamic = [\"version\"]\ndependencies = [\n    # \"frappe~=15.0.0\" # Installed and managed by bench.\n]\n\n[build-system]\nrequires = [\"flit_core >=3.4,<4\"]\nbuild-backend = \"flit_core.buildapi\"\n\n# These dependencies are only installed when developer mode is enabled\n[tool.bench.dev-dependencies]\n# package_name = \"~=1.1.0\"\n\n[tool.ruff]\nline-length = 110\ntarget-version = \"py310\"\n\n[tool.ruff.lint]\nselect = [\n    \"F\",\n    \"E\",\n    \"W\",\n    \"I\",\n    \"UP\",\n    \"B\",\n    \"RUF\",\n]\nignore = [\n    \"B017\", # assertRaises(Exception) - should be more specific\n    \"B018\", # useless expression, not assigned to anything\n    \"B023\", # function doesn't bind loop variable - will have last iteration's value\n    \"B904\", # raise inside except without from\n    \"E101\", # indentation contains mixed spaces and tabs\n    \"E402\", # module level import not at top of file\n    \"E501\", # line too long\n    \"E741\", # ambiguous variable name\n    \"F401\", # \"unused\" imports\n    \"F403\", # can't detect undefined names from * import\n    \"F405\", # can't detect undefined names from * import\n    \"F722\", # syntax error in forward type annotation\n    \"W191\", # indentation contains tabs\n]\ntyping-modules = [\"frappe.types.DF\"]\n\n[tool.ruff.format]\nquote-style = \"double\"\nindent-style = \"tab\"\ndocstring-code-format = true\n"
  },
  {
    "path": "requirements.txt",
    "content": "# frappe -- https://github.com/frappe/frappe is installed via 'bench init'\n# Node version minimum 18.18.0 "
  },
  {
    "path": "setup.py",
    "content": "from setuptools import setup, find_packages\n\nwith open(\"requirements.txt\") as f:\n\tinstall_requires = f.read().strip().split(\"\\n\")\n\n# get version from __version__ variable in ury/__init__.py\nfrom ury import __version__ as version\n\nsetup(\n\tname=\"ury\",\n\tversion=version,\n\tdescription=\"A Complete Restaurant Order Taking Software\",\n\tauthor=\"Tridz Technologies \",\n\tauthor_email=\"info@tridz.com\",\n\tpackages=find_packages(),\n\tzip_safe=False,\n\tinclude_package_data=True,\n\tinstall_requires=install_requires\n)\n"
  },
  {
    "path": "ury/__init__.py",
    "content": "__version__ = \"0.2.1\"\n"
  },
  {
    "path": "ury/config/__init__.py",
    "content": ""
  },
  {
    "path": "ury/config/desktop.py",
    "content": "from frappe import _\n\ndef get_data():\n\treturn [\n\t\t{\n\t\t\t\"module_name\": \"URY\",\n\t\t\t\"type\": \"module\",\n\t\t\t\"label\": _(\"URY\")\n\t\t}\n\t]\n"
  },
  {
    "path": "ury/config/docs.py",
    "content": "\"\"\"\nConfiguration for docs\n\"\"\"\n\n# source_link = \"https://github.com/[org_name]/ury\"\n# headline = \"App that does everything\"\n# sub_heading = \"Yes, you got that right the first time, everything\"\n\ndef get_context(context):\n\tcontext.brand_html = \"URY\"\n"
  },
  {
    "path": "ury/fixtures/client_script.json",
    "content": "[\n {\n  \"docstatus\": 0,\n  \"doctype\": \"Client Script\",\n  \"dt\": \"POS Invoice\",\n  \"enabled\": 0,\n  \"modified\": \"2023-12-19 15:34:46.018469\",\n  \"module\": \"URY\",\n  \"name\": \"Customer mobile number in PoS\",\n  \"script\": \"frappe.ui.form.on('POS Invoice', {\\n    customer(frm) {\\n       \\n        $(document).on('shown.bs.modal', '.modal', function() {\\n            let modal = $(this);\\n\\n            // Hide specific fields and sections\\n            modal.find('.frappe-control[data-fieldname=\\\"customer_group\\\"]').hide();\\n            modal.find('.frappe-control[data-fieldname=\\\"territory\\\"]').hide();\\n            modal.find('.modal-footer .custom-actions button:contains(\\\"Edit Full Form\\\")').remove();\\n\\n            // Copy customer name to mobile number\\n            let nameField = modal.find('.frappe-control[data-fieldname=\\\"customer_name\\\"] input');\\n            let mobileField = modal.find('.frappe-control[data-fieldname=\\\"mobile_number\\\"] input');\\n            \\n            // Copy only if the input is number\\n            if (!isNaN(nameField.val())) {\\n                mobileField.val(nameField.val());\\n                nameField.val('');\\n            }\\n\\n            // Slide down modal body after hiding fields\\n            modal.find('.modal-body, .ui-front').slideDown();\\n\\n            // Set focus to specific fields\\n            modal.find('input[data-fieldname=\\\"customer_name\\\"]').focus();\\n            modal.find('input[data-fieldname=\\\"comment\\\"]').focus();\\n\\n            // Listen for click on the save button\\n            modal.find('.modal-footer .standard-actions .btn-modal-primary').click(function(e) {\\n                if (isNaN(nameField.val() === '')) {\\n                    nameField.val(mobileField.val());\\n                }\\n            });\\n        });\\n    }\\n});\\n\",\n  \"view\": \"Form\"\n },\n {\n  \"docstatus\": 0,\n  \"doctype\": \"Client Script\",\n  \"dt\": \"URY Order\",\n  \"enabled\": 0,\n  \"modified\": \"2023-12-19 15:35:10.075949\",\n  \"module\": \"URY\",\n  \"name\": \"Customer mobile number in ury order\",\n  \"script\": \"frappe.ui.form.on('URY Order', {\\n\\tcustomer_name: function (frm) {\\n\\t\\n\\t     $(document).on('shown.bs.modal', '.modal', function() {\\n            let modal = $(this);\\n\\n            // Hide specific fields and sections\\n            modal.find('.frappe-control[data-fieldname=\\\"customer_group\\\"]').hide();\\n            modal.find('.frappe-control[data-fieldname=\\\"territory\\\"]').hide();\\n            modal.find('.modal-footer .custom-actions button:contains(\\\"Edit Full Form\\\")').remove();\\n\\n            // Copy customer name to mobile number\\n            let nameField = modal.find('.frappe-control[data-fieldname=\\\"customer_name\\\"] input');\\n            let mobileField = modal.find('.frappe-control[data-fieldname=\\\"mobile_number\\\"] input');\\n            \\n            // Copy only if the input is number\\n            if (!isNaN(nameField.val())) {\\n                mobileField.val(nameField.val());\\n                nameField.val('');\\n            }\\n\\n            // Slide down modal body after hiding fields\\n            modal.find('.modal-body, .ui-front').slideDown();\\n\\n            // Set focus to specific fields\\n            modal.find('input[data-fieldname=\\\"customer_name\\\"]').focus();\\n            modal.find('input[data-fieldname=\\\"comment\\\"]').focus();\\n\\n            // Listen for click on the save button\\n            modal.find('.modal-footer .standard-actions .btn-modal-primary').click(function(e) {\\n                if (isNaN(nameField.val() === '')) {\\n                    nameField.val(mobileField.val());\\n                }\\n            });\\n        });\\n\\t    \\n\\t}\\n});\",\n  \"view\": \"Form\"\n }\n]"
  },
  {
    "path": "ury/fixtures/custom_field.json",
    "content": "[\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Branch\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"user\",\n  \"fieldtype\": \"Table\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"branch\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"User\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:37:01.872486\",\n  \"module\": \"URY\",\n  \"name\": \"Branch-user\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY User\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 1,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 1,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Branch\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_aggregators\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"user\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Aggregators Settings\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-14 15:26:54.771637\",\n  \"module\": \"URY\",\n  \"name\": \"Branch-custom_aggregators\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Price List\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_menu\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"currency\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Menu\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:35:11.661873\",\n  \"module\": \"URY\",\n  \"name\": \"Price List-restaurant_menu\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Menu\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Branch\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_aggregator_settings\",\n  \"fieldtype\": \"Table\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_aggregators\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Aggregator Settings\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-13 12:53:03.439452\",\n  \"module\": \"URY\",\n  \"name\": \"Branch-custom_aggregator_settings\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Aggregator Settings\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Customer\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"mobile_number\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 1,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"customer_type\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Mobile Number\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-19 00:39:48.896746\",\n  \"module\": \"URY\",\n  \"name\": \"Customer-mobile_number\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice Item\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_course\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"item_name\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Course\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-28 11:12:40.838571\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice Item-custom_course\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": \"customer.mobile_number\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"mobile_number\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"customer_name\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Mobile Number\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:32:47.485701\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-mobile_number\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": \"customer.mobile_number\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"mobile_number\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"customer_name\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Mobile Number\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:10:38.441697\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-mobile_number\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"Applied for both Sales Invoice and POS Invoice\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Branch\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_no_taxes\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_make_unpaid\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Create Invoice without Tax\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-14 15:26:55.187589\",\n  \"module\": \"URY\",\n  \"name\": \"Branch-custom_no_taxes\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Branch\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_make_unpaid\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_aggregator_settings\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Keep Sales Invoice Unpaid\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-14 15:26:54.894338\",\n  \"module\": \"URY\",\n  \"name\": \"Branch-custom_make_unpaid\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"company_address\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:40:57.426746\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-restaurant_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:40:57.650719\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-restaurant\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Restaurant\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_c10ag\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:40:57.859845\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-column_break_c10ag\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_room\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"user\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Room\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-06-07 11:46:30.749707\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-custom_room\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": \"restaurant.branch\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"branch\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_c10ag\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Branch\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:40:58.061259\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-branch\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Branch\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"user\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:43:24.405830\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-restaurant_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"printer_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"branch\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Printer Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 11:58:59.185765\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-printer_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": \"pos_profile.restaurant\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:43:24.561767\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-restaurant\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Restaurant\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 1,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"eval:doc.qz_print != 1;\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"printer_settings\",\n  \"fieldtype\": \"Table\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"printer_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Printer Settings\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 11:58:59.658627\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-printer_settings\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Printer Settings\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_e3dky\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:43:24.711362\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-column_break_e3dky\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"qz_print\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"printer_settings\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"QZ Print\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 11:58:59.871043\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-qz_print\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice Item\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"comment\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"qty\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Comment\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:38:47.413120\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice Item-comment\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": \"pos_profile.branch\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"branch\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_e3dky\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Branch\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:43:24.854495\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-branch\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Branch\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 1,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"qz_print\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"qz_host\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"qz_print\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"QZ Host\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 11:59:00.067091\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-qz_host\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice Item\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_course\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"comment\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Course\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-28 11:11:51.516114\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice Item-custom_course\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 1,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"section_break_tjhrm\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"qz_host\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"URY pos restrictions \",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-04 11:03:50.097450\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-section_break_tjhrm\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"This role grants permissions for Captain Transfer with access to all tables\\n\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"transfer_role_permissions\",\n  \"fieldtype\": \"Table MultiSelect\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"section_break_tjhrm\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Role Permitted For Captain Transfer\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-04 11:03:50.911595\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-transfer_role_permissions\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Role Permitted\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"Users assigned this role will function as cashiers in URY POS, responsible for managing billing transactions.\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"role_allowed_for_billing\",\n  \"fieldtype\": \"Table MultiSelect\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"transfer_role_permissions\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Role Allowed For Billing\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-05 17:42:25.673638\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-role_allowed_for_billing\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Role Permitted\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": \"0\",\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"table_attention_time\",\n  \"fieldtype\": \"Int\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"role_allowed_for_billing\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Table Attention time\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-02-13 16:30:33.325971\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-table_attention_time\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n    \"allow_in_quick_entry\": 0,\n    \"allow_on_submit\": 0,\n    \"bold\": 0,\n    \"collapsible\": 0,\n    \"collapsible_depends_on\": null,\n    \"columns\": 0,\n    \"default\": \"0\",\n    \"depends_on\": null,\n    \"description\": \"Set a limit for the cashier to view a restricted number of recently paid invoices.\",\n    \"docstatus\": 0,\n    \"doctype\": \"Custom Field\",\n    \"dt\": \"POS Profile\",\n    \"fetch_from\": null,\n    \"fetch_if_empty\": 0,\n    \"fieldname\": \"paid_limit\",\n    \"fieldtype\": \"Int\",\n    \"hidden\": 0,\n    \"hide_border\": 0,\n    \"hide_days\": 0,\n    \"hide_seconds\": 0,\n    \"ignore_user_permissions\": 0,\n    \"ignore_xss_filter\": 0,\n    \"in_global_search\": 0,\n    \"in_list_view\": 0,\n    \"in_preview\": 0,\n    \"in_standard_filter\": 0,\n    \"insert_after\": \"table_attention_time\",\n    \"is_system_generated\": 0,\n    \"is_virtual\": 0,\n    \"label\": \"Show Limited Paid Invoices\",\n    \"length\": 0,\n    \"mandatory_depends_on\": null,\n    \"modified\": \"2024-09-24 14:08:01.442206\",\n    \"module\": \"URY\",\n    \"name\": \"POS Profile-paid_limit\",\n    \"no_copy\": 0,\n    \"non_negative\": 0,\n    \"options\": null,\n    \"permlevel\": 0,\n    \"precision\": \"\",\n    \"print_hide\": 0,\n    \"print_hide_if_no_value\": 0,\n    \"print_width\": null,\n    \"read_only\": 0,\n    \"read_only_depends_on\": null,\n    \"report_hide\": 0,\n    \"reqd\": 0,\n    \"search_index\": 0,\n    \"sort_options\": 0,\n    \"translatable\": 0,\n    \"unique\": 0,\n    \"width\": null\n   },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_bvzw2\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"paid_limit\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-23 11:14:35.908218\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-column_break_bvzw2\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"order_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"return_against\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Order Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:20:59.319547\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-order_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"Users with this role have restricted access to table order functions.\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"role_restricted_for_table_order\",\n  \"fieldtype\": \"Table MultiSelect\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_bvzw2\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Role Restricted For Table Order\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-05 20:16:47.345168\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-role_restricted_for_table_order\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Role Permitted\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": \"\",\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"order_type\",\n  \"fieldtype\": \"Select\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"order_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Order Type\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:20:59.788276\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-order_type\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"\\nDine In\\nPhone In\\nTake Away\\nDelivery\\nAggregators\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"Enables Cashiers to view all statuses (Paid, Consolidated, Return Invoices) in the recent order.\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"view_all_status\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"role_restricted_for_table_order\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Allow Cashier To View All Status\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-23 13:30:17.539810\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-view_all_status\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"waiter\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"order_type\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Waiter\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:00.099273\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-waiter\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"remove_items\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"view_all_status\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Allow Cashier To Edit And Remove Table Order Items\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-23 13:32:45.884420\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-remove_items\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": \"1\",\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"show_image\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"remove_items\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Show Item Image In URY Pos\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-02-09 19:24:46.076630\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-show_image\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"invoice_printed\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"waiter\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Invoice Printed\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-15 10:51:57.172815\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-invoice_printed\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"order_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"amended_from\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Order Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:35.774264\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-order_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_rwbwf\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"invoice_printed\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:00.553123\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-column_break_rwbwf\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"Validate that the previous day\\u2019s POS is closed before opening a new one.\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_daily_pos_close\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"show_image\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Require Daily POS Closing\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-28 11:00:16.547440\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_daily_pos_close\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": \"Enable discount feature in URY POS\",\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_enable_discount\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_daily_pos_close\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Enable Discount\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-12-06 11:00:16.547440\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_enable_discount\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": \"Dine In\",\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"order_type\",\n  \"fieldtype\": \"Select\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"order_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Order Type\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:36.390337\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-order_type\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"\\nDine In\\nTake Away\\nDelivery\\nPhone In\\nAggregators\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"no_of_pax\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_rwbwf\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Pax\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:00.836151\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-no_of_pax\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"waiter\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"order_type\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Waiter\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:36.754796\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-waiter\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"cashier\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"no_of_pax\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Cashier\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:01.162705\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-cashier\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"invoice_created\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 1,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"cashier\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Invoice Created\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:01.912776\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-invoice_created\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_aggregator_id\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"waiter\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Aggregator ID\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-19 13:00:42.714154\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-custom_aggregator_id\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_bc56k\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_aggregator_id\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:37.108132\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-column_break_bc56k\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_aggregator_id\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"invoice_created\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Aggregator ID\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-19 12:55:55.584331\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-custom_aggregator_id\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 1,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"no_of_pax\",\n  \"fieldtype\": \"Int\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_bc56k\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Pax\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:37.563712\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-no_of_pax\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_aggregator_id\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:02.274012\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-restaurant_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"cashier\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"no_of_pax\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Cashier\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:37.936940\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-cashier\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": \"pos_profile.restaurant\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:21:02.583748\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-restaurant\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Restaurant\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_info\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"cashier\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Info\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:38.276026\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-restaurant_info\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": \"pos_profile.branch\",\n  \"fetch_if_empty\": 1,\n  \"fieldname\": \"branch\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Branch\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:22:56.291651\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-branch\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Branch\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant_info\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:38.645738\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-restaurant\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Restaurant\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_table\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"branch\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Table\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:22:56.673077\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-restaurant_table\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Table\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_restaurant_room\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant_table\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Room\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-30 16:42:01.304776\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-custom_restaurant_room\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Room\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"branch\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Branch\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:39.111134\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-branch\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Branch\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_gd1mq\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_restaurant_room\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:22:57.023110\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-column_break_gd1mq\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_table\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"branch\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Table\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:39.546805\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-restaurant_table\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Table\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_restaurant_room\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"restaurant_table\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Room\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-30 16:40:41.609023\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-custom_restaurant_room\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Room\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"column_break_hnrk9\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_restaurant_room\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": null,\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-14 00:31:39.942389\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-column_break_hnrk9\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"arrived_time\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_gd1mq\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Arrived Time\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-29 12:46:13.966106\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-arrived_time\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"total_spend_time\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"arrived_time\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Total Spend Time\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-29 12:46:14.303607\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-total_spend_time\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"arrived_time\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"column_break_hnrk9\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Arrived Time\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-29 12:44:25.623808\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-arrived_time\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"section_break_hllcp\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"total_spend_time\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Cancellation Reason\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-12-04 17:40:35.023429\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-section_break_hllcp\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Sales Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"total_spend_time\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"arrived_time\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Total Spend Time\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-29 12:44:26.110321\",\n  \"module\": \"URY\",\n  \"name\": \"Sales Invoice-total_spend_time\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"eval:doc.docstatus==2\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"cancel_reason\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"section_break_hllcp\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Reason\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-12-04 17:40:35.868052\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-cancel_reason\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"restaurant_prefix\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"allow_discount_change\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Restaurant Prefix For Sales Invoice\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-12-04 14:06:14.125832\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-restaurant_prefix\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"print\",\n  \"fieldtype\": \"Button\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"against_income_account\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Print\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-19 00:54:38.481593\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-print\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_comments\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"print\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Comments\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-02-19 15:53:39.058099\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-custom_comments\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 1,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_multiple_cashier_configuration\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_enable_discount\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Multiple Cashier Configuration\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-03-24 01:35:57.412505\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_multiple_cashier_configuration\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_enable_multiple_cashier\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_multiple_cashier_configuration\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Enable Multiple Cashier\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-03-24 01:36:56.829981\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_enable_multiple_cashier\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile User\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_main_cashier\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 1,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"default\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Main Cashier\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-03-24 14:33:08.806157\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile User-custom_main_cashier\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": \"3\"\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": \"\",\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_rooms\",\n  \"fieldtype\": \"Table\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"user\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Rooms\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-03-25 13:51:40.020513\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-custom_rooms\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Multiple Rooms\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 1,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_sub_pos_close_entry\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"pos_profile\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Sub POS Close Entry\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-03-26 09:41:26.599354\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-custom_sub_pos_close_entry\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Closing Entry Detail\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_closing_amount\",\n  \"fieldtype\": \"Currency\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 1,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"expected_amount\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Closing Amount\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-03-26 10:10:58.233301\",\n  \"module\": \"URY\",\n  \"name\": \"POS Closing Entry Detail-custom_closing_amount\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"company:company_currency\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n },\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_edit_order_type\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_enable_discount\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \" Enable Order Type Edit\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2025-04-11 15:08:20.399135\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_edit_order_type\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"URY Printer Settings\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_print\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 1,\n  \"in_preview\": 1,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"bill\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"KOT Print\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-09-27 13:23:37.572928\",\n  \"module\": \"URY\",\n  \"name\": \"Printer Settings-kot\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"custom_kot_print\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"URY Printer Settings\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_print_format\",\n  \"fieldtype\": \"Link\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"printer\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"KOT Print Format\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-09 13:03:26.991856\",\n  \"module\": \"URY\",\n  \"name\": \"Printer Settings-kot_print_format_\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"Print Format\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"eval:doc.parenttype=='URY Production Unit' && doc.custom_kot_print == 1\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"URY Printer Settings\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_block_takeaway_kot\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_print_format\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Block Takeaway KOT\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": \"\",\n  \"modified\": \"2024-01-17 02:00:22.569584\",\n  \"module\": \"URY\",\n  \"name\": \"URY Printer Settings-custom_block_takeaway_kot\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_settings\",\n  \"fieldtype\": \"Section Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"branch\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"KOT Settings\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-10-01 21:07:14.402860\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_kot_settings\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 1,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_ury_last_aggregator_invoice\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 1,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_ury_last_invoice\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"URY Last Aggregator Invoice\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-22 18:40:57.911370\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-custom_ury_last_aggregator_invoice\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 1,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Opening Entry\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_ury_last_invoice\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 1,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"quality_checklist\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"URY Last Invoice\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-22 18:40:45.758174\",\n  \"module\": \"URY\",\n  \"name\": \"POS Opening Entry-custom_ury_last_invoice\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 1,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_naming_series\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_settings\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"URY KOT Naming Series\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-10-01 21:08:17.416512\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-kot_naming_series\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_alert\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_naming_series\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Enable KOT Audio Alert\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-12-09 21:30:41.399221\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_kot_alert\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"custom_kot_alert\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_alert_sound\",\n  \"fieldtype\": \"Attach\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_alert\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"KOT alert sound\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": \"custom_kot_alert\",\n  \"modified\": \"2023-12-11 09:51:31.430739\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_kot_alert_sound\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_cl\",\n  \"fieldtype\": \"Column Break\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_alert_sound\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-12-12 12:30:14.254898\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_cl\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_kot_warning_time\",\n  \"fieldtype\": \"Int\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_cl\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"KOT Warning Time\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": \"\",\n  \"modified\": \"2023-12-13 11:37:13.317018\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_kot_warning_time\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_notify_kot_delay\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_warning_time\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Notify KOT Delay\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2023-12-12 12:32:02.111078\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_notify_kot_delay\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": \"custom_notify_kot_delay\",\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_recipients\",\n  \"fieldtype\": \"Table\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_notify_kot_delay\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Recipients\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": \"custom_notify_kot_delay\",\n  \"modified\": \"2023-12-12 12:38:56.235040\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_recipients\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"URY Notification Recipient\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Profile\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_reset_order_number_daily\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_kot_warning_time\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Reset Order Number Daily\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-23 10:50:44.420498\",\n  \"module\": \"URY\",\n  \"name\": \"POS Profile-custom_reset_order_number_daily\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"POS Invoice\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_ury_order_number\",\n  \"fieldtype\": \"Data\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"status\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"URY Order Number\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-23 14:37:02.717063\",\n  \"module\": \"URY\",\n  \"name\": \"POS Invoice-custom_ury_order_number\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"URY Menu Course\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_indicate_in_kds\",\n  \"fieldtype\": \"Check\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"custom_serving_priority\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Indicate In KDS\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-28 16:22:18.737726\",\n  \"module\": \"URY\",\n  \"name\": \"URY Menu Course-custom_indicate_in_kds\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"URY Menu Course\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"custom_serving_priority\",\n  \"fieldtype\": \"Int\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 1,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"course\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Serving Priority\",\n  \"length\": 0,\n  \"link_filters\": null,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-08-28 11:28:26.229527\",\n  \"module\": \"URY\",\n  \"name\": \"URY Menu Course-custom_serving_priority\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"show_dashboard\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n},\n{\n    \"allow_in_quick_entry\": 0,\n    \"allow_on_submit\": 0,\n    \"bold\": 0,\n    \"collapsible\": 0,\n    \"collapsible_depends_on\": null,\n    \"columns\": 0,\n    \"default\": null,\n    \"depends_on\": null,\n    \"description\": null,\n    \"docstatus\": 0,\n    \"doctype\": \"Custom Field\",\n    \"dt\": \"POS Profile\",\n    \"fetch_from\": null,\n    \"fetch_if_empty\": 0,\n    \"fieldname\": \"custom_enable_kot_reprint\",\n    \"fieldtype\": \"Check\",\n    \"hidden\": 0,\n    \"hide_border\": 0,\n    \"hide_days\": 0,\n    \"hide_seconds\": 0,\n    \"ignore_user_permissions\": 0,\n    \"ignore_xss_filter\": 0,\n    \"in_global_search\": 0,\n    \"in_list_view\": 0,\n    \"in_preview\": 0,\n    \"in_standard_filter\": 0,\n    \"insert_after\": \"custom_column_break_wwq3q\",\n    \"is_system_generated\": 0,\n    \"is_virtual\": 0,\n    \"label\": \"Enable KOT Reprint\",\n    \"length\": 0,\n    \"link_filters\": null,\n    \"mandatory_depends_on\": null,\n    \"modified\": \"2025-02-19 12:51:46.708745\",\n    \"module\": \"URY\",\n    \"name\": \"POS Profile-custom_enable_kot_reprint\",\n    \"no_copy\": 0,\n    \"non_negative\": 0,\n    \"options\": null,\n    \"permlevel\": 0,\n    \"placeholder\": null,\n    \"precision\": \"\",\n    \"print_hide\": 0,\n    \"print_hide_if_no_value\": 0,\n    \"print_width\": null,\n    \"read_only\": 0,\n    \"read_only_depends_on\": null,\n    \"report_hide\": 0,\n    \"reqd\": 0,\n    \"search_index\": 0,\n    \"show_dashboard\": 0,\n    \"sort_options\": 0,\n    \"translatable\": 0,\n    \"unique\": 0,\n    \"width\": null\n  },\n  {\n    \"allow_in_quick_entry\": 0,\n    \"allow_on_submit\": 0,\n    \"bold\": 0,\n    \"collapsible\": 0,\n    \"collapsible_depends_on\": null,\n    \"columns\": 0,\n    \"default\": null,\n    \"depends_on\": \"eval:doc.custom_enable_kot_reprint\",\n    \"description\": null,\n    \"docstatus\": 0,\n    \"doctype\": \"Custom Field\",\n    \"dt\": \"POS Profile\",\n    \"fetch_from\": null,\n    \"fetch_if_empty\": 0,\n    \"fieldname\": \"custom_parcel_order_printer\",\n    \"fieldtype\": \"Link\",\n    \"hidden\": 0,\n    \"hide_border\": 0,\n    \"hide_days\": 0,\n    \"hide_seconds\": 0,\n    \"ignore_user_permissions\": 0,\n    \"ignore_xss_filter\": 0,\n    \"in_global_search\": 0,\n    \"in_list_view\": 0,\n    \"in_preview\": 0,\n    \"in_standard_filter\": 0,\n    \"insert_after\": \"custom_enable_kot_reprint\",\n    \"is_system_generated\": 0,\n    \"is_virtual\": 0,\n    \"label\": \"Parcel Order Printer\",\n    \"length\": 0,\n    \"link_filters\": null,\n    \"mandatory_depends_on\": null,\n    \"modified\": \"2025-02-19 12:53:18.058825\",\n    \"module\": \"URY\",\n    \"name\": \"POS Profile-custom_parcel_order_printer\",\n    \"no_copy\": 0,\n    \"non_negative\": 0,\n    \"options\": \"Network Printer Settings\",\n    \"permlevel\": 0,\n    \"placeholder\": null,\n    \"precision\": \"\",\n    \"print_hide\": 0,\n    \"print_hide_if_no_value\": 0,\n    \"print_width\": null,\n    \"read_only\": 0,\n    \"read_only_depends_on\": null,\n    \"report_hide\": 0,\n    \"reqd\": 0,\n    \"search_index\": 0,\n    \"show_dashboard\": 0,\n    \"sort_options\": 0,\n    \"translatable\": 0,\n    \"unique\": 0,\n    \"width\": null\n  },\n  {\n    \"allow_in_quick_entry\": 0,\n    \"allow_on_submit\": 0,\n    \"bold\": 0,\n    \"collapsible\": 0,\n    \"collapsible_depends_on\": null,\n    \"columns\": 0,\n    \"default\": null,\n    \"depends_on\": null,\n    \"description\": null,\n    \"docstatus\": 0,\n    \"doctype\": \"Custom Field\",\n    \"dt\": \"POS Profile\",\n    \"fetch_from\": null,\n    \"fetch_if_empty\": 0,\n    \"fieldname\": \"custom_column_break_wwq3q\",\n    \"fieldtype\": \"Column Break\",\n    \"hidden\": 0,\n    \"hide_border\": 0,\n    \"hide_days\": 0,\n    \"hide_seconds\": 0,\n    \"ignore_user_permissions\": 0,\n    \"ignore_xss_filter\": 0,\n    \"in_global_search\": 0,\n    \"in_list_view\": 0,\n    \"in_preview\": 0,\n    \"in_standard_filter\": 0,\n    \"insert_after\": \"custom_recipients\",\n    \"is_system_generated\": 0,\n    \"is_virtual\": 0,\n    \"label\": \"\",\n    \"length\": 0,\n    \"link_filters\": null,\n    \"mandatory_depends_on\": null,\n    \"modified\": \"2025-02-19 12:54:21.578630\",\n    \"module\": \"URY\",\n    \"name\": \"POS Profile-custom_column_break_wwq3q\",\n    \"no_copy\": 0,\n    \"non_negative\": 0,\n    \"options\": null,\n    \"permlevel\": 0,\n    \"placeholder\": null,\n    \"precision\": \"\",\n    \"print_hide\": 0,\n    \"print_hide_if_no_value\": 0,\n    \"print_width\": null,\n    \"read_only\": 0,\n    \"read_only_depends_on\": null,\n    \"report_hide\": 0,\n    \"reqd\": 0,\n    \"search_index\": 0,\n    \"show_dashboard\": 0,\n    \"sort_options\": 0,\n    \"translatable\": 0,\n    \"unique\": 0,\n    \"width\": null\n  },\n  {\n    \"allow_in_quick_entry\": 0,\n    \"allow_on_submit\": 0,\n    \"bold\": 0,\n    \"collapsible\": 0,\n    \"collapsible_depends_on\": null,\n    \"columns\": 0,\n    \"default\": null,\n    \"depends_on\": \"eval:doc.custom_enable_kot_reprint\",\n    \"description\": null,\n    \"docstatus\": 0,\n    \"doctype\": \"Custom Field\",\n    \"dt\": \"POS Profile\",\n    \"fetch_from\": null,\n    \"fetch_if_empty\": 0,\n    \"fieldname\": \"custom_table_order_printer\",\n    \"fieldtype\": \"Link\",\n    \"hidden\": 0,\n    \"hide_border\": 0,\n    \"hide_days\": 0,\n    \"hide_seconds\": 0,\n    \"ignore_user_permissions\": 0,\n    \"ignore_xss_filter\": 0,\n    \"in_global_search\": 0,\n    \"in_list_view\": 0,\n    \"in_preview\": 0,\n    \"in_standard_filter\": 0,\n    \"insert_after\": \"custom_parcel_order_printer\",\n    \"is_system_generated\": 0,\n    \"is_virtual\": 0,\n    \"label\": \"Table Order Printer\",\n    \"length\": 0,\n    \"link_filters\": null,\n    \"mandatory_depends_on\": null,\n    \"modified\": \"2025-02-19 12:59:09.653916\",\n    \"module\": \"URY\",\n    \"name\": \"POS Profile-custom_table_order_printer\",\n    \"no_copy\": 0,\n    \"non_negative\": 0,\n    \"options\": \"Network Printer Settings\",\n    \"permlevel\": 0,\n    \"placeholder\": null,\n    \"precision\": \"\",\n    \"print_hide\": 0,\n    \"print_hide_if_no_value\": 0,\n    \"print_width\": null,\n    \"read_only\": 0,\n    \"read_only_depends_on\": null,\n    \"report_hide\": 0,\n    \"reqd\": 0,\n    \"search_index\": 0,\n    \"show_dashboard\": 0,\n    \"sort_options\": 0,\n    \"translatable\": 0,\n    \"unique\": 0,\n    \"width\": null\n  },\n  {\n    \"allow_in_quick_entry\": 0,\n    \"allow_on_submit\": 0,\n    \"bold\": 0,\n    \"collapsible\": 0,\n    \"collapsible_depends_on\": null,\n    \"columns\": 0,\n    \"default\": null,\n    \"depends_on\": \"eval:doc.custom_enable_kot_reprint\",\n    \"description\": null,\n    \"docstatus\": 0,\n    \"doctype\": \"Custom Field\",\n    \"dt\": \"POS Profile\",\n    \"fetch_from\": null,\n    \"fetch_if_empty\": 0,\n    \"fieldname\": \"custom_reprint_kot_format\",\n    \"fieldtype\": \"Link\",\n    \"hidden\": 0,\n    \"hide_border\": 0,\n    \"hide_days\": 0,\n    \"hide_seconds\": 0,\n    \"ignore_user_permissions\": 0,\n    \"ignore_xss_filter\": 0,\n    \"in_global_search\": 0,\n    \"in_list_view\": 0,\n    \"in_preview\": 0,\n    \"in_standard_filter\": 0,\n    \"insert_after\": \"custom_table_order_printer\",\n    \"is_system_generated\": 0,\n    \"is_virtual\": 0,\n    \"label\": \"Reprint KOT Format\",\n    \"length\": 0,\n    \"link_filters\": null,\n    \"mandatory_depends_on\": null,\n    \"modified\": \"2025-02-19 14:34:30.828917\",\n    \"module\": \"URY\",\n    \"name\": \"POS Profile-custom_reprint_kot_format\",\n    \"no_copy\": 0,\n    \"non_negative\": 0,\n    \"options\": \"Print Format\",\n    \"permlevel\": 0,\n    \"placeholder\": null,\n    \"precision\": \"\",\n    \"print_hide\": 0,\n    \"print_hide_if_no_value\": 0,\n    \"print_width\": null,\n    \"read_only\": 0,\n    \"read_only_depends_on\": null,\n    \"report_hide\": 0,\n    \"reqd\": 0,\n    \"search_index\": 0,\n    \"show_dashboard\": 0,\n    \"sort_options\": 0,\n    \"translatable\": 0,\n    \"unique\": 0,\n    \"width\": null\n  },\n  {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Employee\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"payment_type\",\n  \"fieldtype\": \"Select\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"salary_currency\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Payment Type\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-09 14:49:44.365494\",\n  \"module\": \"URY\",\n  \"name\": \"Employee-payment_type\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": \"\\nDaily Wage\\nSalary\",\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n  },\n  {\n  \"allow_in_quick_entry\": 0,\n  \"allow_on_submit\": 0,\n  \"bold\": 0,\n  \"collapsible\": 0,\n  \"collapsible_depends_on\": null,\n  \"columns\": 0,\n  \"default\": null,\n  \"depends_on\": null,\n  \"description\": null,\n  \"docstatus\": 0,\n  \"doctype\": \"Custom Field\",\n  \"dt\": \"Employee\",\n  \"fetch_from\": null,\n  \"fetch_if_empty\": 0,\n  \"fieldname\": \"payment_amount\",\n  \"fieldtype\": \"Currency\",\n  \"hidden\": 0,\n  \"hide_border\": 0,\n  \"hide_days\": 0,\n  \"hide_seconds\": 0,\n  \"ignore_user_permissions\": 0,\n  \"ignore_xss_filter\": 0,\n  \"in_global_search\": 0,\n  \"in_list_view\": 0,\n  \"in_preview\": 0,\n  \"in_standard_filter\": 0,\n  \"insert_after\": \"payment_type\",\n  \"is_system_generated\": 0,\n  \"is_virtual\": 0,\n  \"label\": \"Payment Amount\",\n  \"length\": 0,\n  \"mandatory_depends_on\": null,\n  \"modified\": \"2024-01-09 15:27:00.294408\",\n  \"module\": \"URY\",\n  \"name\": \"Employee-payment_amount\",\n  \"no_copy\": 0,\n  \"non_negative\": 0,\n  \"options\": null,\n  \"permlevel\": 0,\n  \"precision\": \"\",\n  \"print_hide\": 0,\n  \"print_hide_if_no_value\": 0,\n  \"print_width\": null,\n  \"read_only\": 0,\n  \"read_only_depends_on\": null,\n  \"report_hide\": 0,\n  \"reqd\": 0,\n  \"search_index\": 0,\n  \"sort_options\": 0,\n  \"translatable\": 0,\n  \"unique\": 0,\n  \"width\": null\n  }\n]"
  },
  {
    "path": "ury/fixtures/custom_html_block.json",
    "content": "\n[\n    {\n     \"docstatus\": 0,\n     \"doctype\": \"Custom HTML Block\",\n     \"html\":\"<a href=\\\"/pos\\\">\\n    <img class=\\\"uryposlogo\\\" src=\\\"/assets/ury/Images/URY-POS.jpg\\\"/>\\n</a>\",\n     \"modified\": \"2025-07-24 16:50:40.049812\",\n     \"name\": \"URY POS\",\n     \"private\": 0,\n     \"roles\": [],\n     \"script\": \"\",\n     \"style\": \".uryposlogo {\\n    height: 37px;\\n}\"\n    },\n    {\n     \"docstatus\": 0,\n     \"doctype\": \"Custom HTML Block\",\n     \"html\": \"<a href=\\\"/urypos\\\" class=\\\"container\\\" ><lable class=\\\"urypos\\\">POS V1</lable> </a>\",\n     \"modified\" : \"2025-07-24 16:59:46.440258\",\n     \"name\": \"POS V1\",\n     \"private\": 0,\n     \"roles\": [],\n     \"script\": \"\",\n     \"style\": \".container {\\n    min-height:40px;\\n    display:flex;\\n    align-items:center;\\n    justify-content:center;\\n}\\n\\n.uryposlogo {\\n  font-size:20px;\\n}\"\n    }\n]\n"
  },
  {
    "path": "ury/fixtures/property_setter.json",
    "content": "[\n {\n  \"default_value\": null,\n  \"doc_type\": \"POS Closing Entry Detail\",\n  \"docstatus\": 0,\n  \"doctype\": \"Property Setter\",\n  \"doctype_or_field\": \"DocField\",\n  \"field_name\": \"closing_amount\",\n  \"is_system_generated\": 0,\n  \"modified\": \"2025-03-26 10:17:33.707296\",\n  \"module\": \"URY\",\n  \"name\": \"POS Closing Entry Detail-closing_amount-label\",\n  \"property\": \"label\",\n  \"property_type\": \"Data\",\n  \"row_name\": null,\n  \"value\": \"Total Closing Amount\"\n }\n]"
  },
  {
    "path": "ury/fixtures/role.json",
    "content": "[\n {\n  \"bulk_actions\": 1,\n  \"dashboard\": 1,\n  \"desk_access\": 1,\n  \"disabled\": 0,\n  \"docstatus\": 0,\n  \"doctype\": \"Role\",\n  \"form_sidebar\": 1,\n  \"home_page\": null,\n  \"is_custom\": 0,\n  \"list_sidebar\": 1,\n  \"modified\": \"2023-09-21 22:14:15.855249\",\n  \"name\": \"URY Manager\",\n  \"notifications\": 1,\n  \"restrict_to_domain\": null,\n  \"role_name\": \"URY Manager\",\n  \"search_bar\": 1,\n  \"timeline\": 1,\n  \"two_factor_auth\": 0,\n  \"view_switcher\": 1\n },\n {\n  \"bulk_actions\": 1,\n  \"dashboard\": 1,\n  \"desk_access\": 1,\n  \"disabled\": 0,\n  \"docstatus\": 0,\n  \"doctype\": \"Role\",\n  \"form_sidebar\": 1,\n  \"home_page\": null,\n  \"is_custom\": 0,\n  \"list_sidebar\": 1,\n  \"modified\": \"2023-09-21 22:27:18.023006\",\n  \"name\": \"URY Captain\",\n  \"notifications\": 1,\n  \"restrict_to_domain\": null,\n  \"role_name\": \"URY Captain\",\n  \"search_bar\": 1,\n  \"timeline\": 1,\n  \"two_factor_auth\": 0,\n  \"view_switcher\": 1\n },\n {\n  \"bulk_actions\": 1,\n  \"dashboard\": 1,\n  \"desk_access\": 1,\n  \"disabled\": 0,\n  \"docstatus\": 0,\n  \"doctype\": \"Role\",\n  \"form_sidebar\": 1,\n  \"home_page\": null,\n  \"is_custom\": 0,\n  \"list_sidebar\": 1,\n  \"modified\": \"2023-12-19 13:07:16.828200\",\n  \"name\": \"URY Cashier\",\n  \"notifications\": 1,\n  \"restrict_to_domain\": null,\n  \"role_name\": \"URY Cashier\",\n  \"search_bar\": 1,\n  \"timeline\": 1,\n  \"two_factor_auth\": 0,\n  \"view_switcher\": 1\n }\n]"
  },
  {
    "path": "ury/hooks.py",
    "content": "from . import __version__ as app_version\n\napp_name = \"ury\"\napp_title = \"URY\"\napp_publisher = \"Tridz Technologies Pvt. Ltd\"\napp_description = \"A Complete Restaurant Order Taking Software\"\napp_email = \"info@tridz.com\"\napp_license = \"MIT\"\napp_logo_url = \"/assets/ury/Images/ury-logo.jpg\"\napp_icon_title = \"URY\"\nrequired_apps = [\"erpnext\"]\n# Includes in <head>\n# ------------------\nadd_to_apps_screen = [\n  {\n    \"name\": \"ury\",\n    \"logo\": \"/assets/ury/Images/ury.png\",\n    \"title\": \"URY\",\n    \"route\": \"/app/ury\",\n    \"has_permission\": \"ury.permission.check_app_permission\"\n  }\n]\n# include js, css files in header of desk.html\n# app_include_css = \"/assets/ury/css/ury.css\"\napp_include_js = [\n    \"/assets/ury/js/quick_entry.js\",\n    \"/assets/ury/js/pos_print.js\",\n    \"/assets/ury/js/restrict_qty_edit_pos.js\",\n    \"/assets/ury/js/ury_pos_kot.js\"\n]\n\n# include js, css files in header of web template\n# web_include_css = \"/assets/ury/css/ury.css\"\n# web_include_js = \"/assets/ury/js/ury.js\"\n\n# include custom scss in every website theme (without file extension \".scss\")\n# website_theme_scss = \"ury/public/scss/website\"\n\n# include js, css files in header of web form\n# webform_include_js = {\"doctype\": \"public/js/doctype.js\"}\n# webform_include_css = {\"doctype\": \"public/css/doctype.css\"}\n\n# include js in page\npage_js = {\"point-of-sale\": [\"public/js/pos_extend.js\"]}\n\n# include js in doctype views\n# doctype_js = {\"POS Invoive\" : \"public/js/pos_print.js\"}\n# doctype_list_js = {\"doctype\" : \"public/js/doctype_list.js\"}\n# doctype_tree_js = {\"doctype\" : \"public/js/doctype_tree.js\"}\n# doctype_calendar_js = {\"doctype\" : \"public/js/doctype_calendar.js\"}\n\n# Splash Image in Website Settings\nwebsite_context = {\"splash_image\": \"/assets/ury/Images/ury-logo.jpg\"}\n\nwebsite_route_rules = [\n    {\"from_route\": \"/pos/<path:app_path>\", \"to_route\": \"pos\"},\n    {\"from_route\": \"/urypos/<path:app_path>\", \"to_route\": \"urypos\"},\n    {\"from_route\": \"/URYMosaic/<path:app_path>\", \"to_route\": \"URYMosaic\"},\n]\n# Home Pages\n# ----------\n\n# application home page (will override Website Settings)\n# home_page = \"login\"\n\n# website user home page (by Role)\n# role_home_page = {\n# \t\"Role\": \"home_page\"\n# }\n\n# Generators\n# ----------\n\n# automatically create page for each record of this doctype\n# website_generators = [\"Web Page\"]\n\n# Jinja\n# ----------\n\n# add methods and filters to jinja environment\n# jinja = {\n# \t\"methods\": \"ury.utils.jinja_methods\",\n# \t\"filters\": \"ury.utils.jinja_filters\"\n# }\n\n# Installation\n# ------------\n\n# before_install = \"ury.install.before_install\"\n# after_install = \"ury.install.after_install\"\n\n# Uninstallation\n# ------------\n\nbefore_uninstall = \"ury.uninstall.uninstall\"\n# after_uninstall = \"ury.uninstall.before_uninstall\"\n\n# Desk Notifications\n# ------------------\n# See frappe.core.notifications.get_notification_config\n\n# notification_config = \"ury.notifications.get_notification_config\"\n\n# Permissions\n# -----------\n# Permissions evaluated in scripted ways\n\n# permission_query_conditions = {\n# \t\"Event\": \"frappe.desk.doctype.event.event.get_permission_query_conditions\",\n# }\n#\n# has_permission = {\n# \t\"Event\": \"frappe.desk.doctype.event.event.has_permission\",\n# }\n\n# DocType Class\n# ---------------\n# Override standard doctype classes\n\n# override_doctype_class = {\n# \t\"ToDo\": \"custom_app.overrides.CustomToDo\"\n# }\n\n# Document Events\n# ---------------\n# Hook on document methods and events\n\ndoc_events = {\n    \"POS Invoice\": {\n        \"before_insert\": \"ury.ury.hooks.ury_pos_invoice.before_insert\",\n        \"validate\": \"ury.ury.hooks.ury_pos_invoice.validate\",\n        \"after_insert\":\"ury.ury.api.ury_kot_order_number.set_order_number\",\n        \"before_submit\": \"ury.ury.hooks.ury_pos_invoice.before_submit\",\n        \"on_cancel\": \"ury.ury.hooks.ury_pos_invoice.on_trash\",\n        \"on_trash\": \"ury.ury.hooks.ury_pos_invoice.on_trash\",\n    },\n    \"POS Profile\": {\"validate\": \"ury.ury.hooks.ury_pos_profile.validate\"},\n    \"Sales Invoice\": {\n        \"before_insert\": \"ury.ury.hooks.ury_sales_invoice.before_insert\",\n        \"on_update\":\"ury.ury.hooks.ury_sales_invoice.on_update\",\n        },\n    \"Item\": {\"validate\": \"ury.ury.hooks.ury_item.validate\"},\n    \"POS Opening Entry\": {\n        \"validate\":\"ury.ury.hooks.ury_pos_opening_entry.set_cashier_room\",\n        \"before_save\": \"ury.ury.hooks.ury_pos_opening_entry.before_save\",\n        \"before_insert\":\"ury.ury.api.ury_kot_order_number.set_last_invoice_in_pos_open\",\n        },\n    \"POS Closing Entry\": {\n        \"before_save\": \"ury.ury.hooks.ury_pos_closing_entry.before_save\",\n        \"validate\":\"ury.ury.hooks.ury_pos_closing_entry.validate\"\n        },\n    \"URY Menu Course\": {\n\t\t\"validate\": \"ury.ury.api.ury_menu_course_validation.validate_priority\",\n\t}    \n}\n\n# Scheduled Tasks\n# ---------------\n\nscheduler_events = {\n    \"cron\":{\n\t\t\"* * * * *\":[\n\t\t\t\"ury.ury.api.ury_kot_validation.kotValidationThread\"\n\t\t]\n\t}\n# \t\"all\": [\n# \t\t\"ury.tasks.all\"\n# \t],\n# \t\"daily\": [\n# \t\t\"ury.tasks.daily\"\n# \t],\n# \t\"hourly\": [\n# \t\t\"ury.tasks.hourly\"\n# \t],\n# \t\"weekly\": [\n# \t\t\"ury.tasks.weekly\"\n# \t],\n# \t\"monthly\": [\n# \t\t\"ury.tasks.monthly\"\n# \t],\n}\n\n# Testing\n# -------\n\n# before_tests = \"ury.install.before_tests\"\n\n# Overriding Methods\n# ------------------------------\n#\n# override_whitelisted_methods = {\n# \t\"frappe.desk.doctype.event.event.get_events\": \"ury.event.get_events\"\n# }\n#\n# each overriding function accepts a `data` argument;\n# generated from the base implementation of the doctype dashboard,\n# along with any modifications made in other Frappe apps\n# override_doctype_dashboards = {\n# \t\"Task\": \"ury.task.get_dashboard_data\"\n# }\n\n# exempt linked doctypes from being automatically cancelled\n#\n# auto_cancel_exempted_doctypes = [\"Auto Repeat\"]\n\n# Ignore links to specified DocTypes when deleting documents\n# -----------------------------------------------------------\n\n# ignore_links_on_delete = [\"Communication\", \"ToDo\"]\n\n# Request Events\n# ----------------\n# before_request = [\"ury.utils.before_request\"]\n# after_request = [\"ury.utils.after_request\"]\n\n# Job Events\n# ----------\n# before_job = [\"ury.utils.before_job\"]\n# after_job = [\"ury.utils.after_job\"]\n\n# User Data Protection\n# --------------------\n\n# user_data_fields = [\n# \t{\n# \t\t\"doctype\": \"{doctype_1}\",\n# \t\t\"filter_by\": \"{filter_by}\",\n# \t\t\"redact_fields\": [\"{field_1}\", \"{field_2}\"],\n# \t\t\"partial\": 1,\n# \t},\n# \t{\n# \t\t\"doctype\": \"{doctype_2}\",\n# \t\t\"filter_by\": \"{filter_by}\",\n# \t\t\"partial\": 1,\n# \t},\n# \t{\n# \t\t\"doctype\": \"{doctype_3}\",\n# \t\t\"strict\": False,\n# \t},\n# \t{\n# \t\t\"doctype\": \"{doctype_4}\"\n# \t}\n# ]\n\n# Authentication and authorization\n# --------------------------------\n\n# auth_hooks = [\n# \t\"ury.auth.validate\"\n# ]\n\nfixtures = [\n    {\n        \"doctype\": \"Custom Field\",\n        \"filters\": [\n            [\n                \"name\",\n                \"in\",\n                {\n                    \"Customer-mobile_number\",\n                    \"POS Invoice-mobile_number\",\n                    \"POS Invoice-order_info\",\n                    \"POS Invoice-order_type\",\n                    \"POS Invoice-waiter\",\n                    \"POS Invoice-column_break_rwbwf\",\n                    \"POS Invoice-no_of_pax\",\n                    \"POS Invoice-cashier\",\n                    \"POS Invoice-invoice_printed\",\n                    \"POS Invoice-invoice_created\",\n                    \"POS Invoice-custom_aggregator_id\",\n                    \"POS Invoice-restaurant_info\",\n                    \"POS Invoice-restaurant\",\n                    \"POS Invoice-branch\",\n                    \"POS Invoice-print\",\n                    \"POS Invoice-restaurant_table\",\n                    \"POS Invoice-custom_restaurant_room\",\n                    \"POS Invoice-column_break_gd1mq\",\n                    \"POS Invoice-arrived_time\",\n                    \"POS Invoice-total_spend_time\",\n                    \"POS Invoice-section_break_hllcp\",\n                    \"POS Invoice-cancel_reason\",\n                    \"POS Invoice Item-comment\",\n                    \"POS Invoice Item-custom_course\",\n                    \"Sales Invoice-mobile_number\",\n                    \"Sales Invoice-order_info\",\n                    \"Sales Invoice-order_type\",\n                    \"Sales Invoice-waiter\",\n                    \"Sales Invoice-column_break_bc56k\",\n                    \"Sales Invoice-no_of_pax\",\n                    \"Sales Invoice-cashier\",\n                    \"Sales Invoice-restaurant_info\",\n                    \"Sales Invoice-restaurant\",\n                    \"Sales Invoice-branch\",\n                    \"Sales Invoice-restaurant_table\",\n                    \"Sales Invoice-custom_restaurant_room\",\n                    \"Sales Invoice-column_break_hnrk9\",\n                    \"Sales Invoice-arrived_time\",\n                    \"Sales Invoice-total_spend_time\",\n                    \"Sales Invoice-custom_aggregator_id\",\n                    \"Sales Invoice Item-custom_course\",\n                    \"POS Profile-restaurant_info\",\n                    \"POS Profile-restaurant\",\n                    \"POS Profile-column_break_c10ag\",\n                    \"POS Profile-branch\",\n                    \"POS Profile-printer_info\",\n                    \"POS Profile-printer_settings\",\n                    \"POS Profile-qz_print\",\n                    \"POS Profile-qz_host\",\n                    \"POS Profile-section_break_tjhrm\",\n                    \"POS Profile-transfer_role_permissions\",\n                    \"POS Profile-role_allowed_for_billing\",\n                    \"POS Profile-column_break_bvzw2\",\n                    \"POS Profile-role_restricted_for_table_order\",\n                    \"POS Profile-view_all_status\",\n                    \"POS Profile-remove_items\",\n                    \"POS Profile-restaurant_prefix\",\n                    \"POS Profile-show_image\",\n                    \"POS Profile-custom_daily_pos_close\",\n                    \"POS Profile-paid_limit\",\n                    \"POS Profile-table_attention_time\",\n                    \"POS Opening Entry-restaurant_info\",\n                    \"POS Opening Entry-restaurant\",\n                    \"POS Opening Entry-column_break_e3dky\",\n                    \"POS Opening Entry-branch\",\n                    \"POS Opening Entry-custom_room\",\n                    \"Branch-user\",\n                    \"Branch-custom_aggregator_settings\",\n                    \"Branch-custom_aggregators\",\n                    \"Branch-custom_make_unpaid\",\n                    \"Branch-custom_no_taxes\",\n                    \"Price List-restaurant_menu\",\n                    \"POS Profile-custom_enable_discount\",\n                    \"POS Invoice-custom_comments\",\n                    \"POS Profile-custom_multiple_cashier_configuration\",\n                    \"POS Profile-custom_enable_multiple_cashier\",\n                    \"POS Profile User-custom_main_cashier\",\n                    \"POS Opening Entry-custom_rooms\",\n                    \"POS Opening Entry-custom_sub_pos_close_entry\",\n                    \"POS Closing Entry Detail-custom_closing_amount\",\n                    \"POS Profile-custom_edit_order_type\",\n                    \"Printer Settings-kot_print_format_\",\n                    \"Printer Settings-kot\",\n                    \"POS Profile-kot_naming_series\",\n                    \"POS Profile-custom_kot_settings\",\n                    \"POS Profile-custom_kot_alert\",\n                    \"POS Profile-custom_kot_alert_sound\",\n                    \"POS Profile-custom_kot_warning_time\",\n                    \"POS Profile-custom_cl\",\n                    \"POS Profile-custom_notify_kot_delay\",\n                    \"POS Profile-custom_recipients\",\n                    \"URY Printer Settings-custom_block_takeaway_kot\",\n                    \"POS Opening Entry-custom_ury_last_invoice\",\n                    \"POS Opening Entry-custom_ury_last_aggregator_invoice\",\n                    \"POS Profile-custom_reset_order_number_daily\",\n                    \"POS Invoice-custom_ury_order_number\",\n                    \"URY Menu Course-custom_serving_priority\",\n                    \"URY Menu Course-custom_indicate_in_kds\",\n                    \"POS Profile-custom_enable_kot_reprint\",\n                    \"POS Profile-custom_parcel_order_printer\",\n                    \"POS Profile-custom_column_break_wwq3q\",\n                    \"POS Profile-custom_table_order_printer\",\n                    \"POS Profile-custom_reprint_kot_format\",\n                    \"Employee-payment_amount\",\n                    \"Employee-payment_type\"\n                },\n            ]\n        ],\n    },\n    {\n        \"dt\": \"Property Setter\",\n        \"filters\": [\n            [\n                \"name\",\n                \"in\",\n                {\n                    \"POS Closing Entry Detail-closing_amount-label\"\n                }\n            ]\n        ],\n    },\n    {\"dt\": \"Role\", \"filters\": [[\"role_name\", \"like\", \"URY %\"]]},\n    \"Client Script\",\n]\n"
  },
  {
    "path": "ury/install.py",
    "content": "import click\n\nfrom ury.setup import after_install as setup\n\n\ndef after_install():\n    try:\n        print(\"Setting up URY...\")\n        setup()\n        \n        click.secho(\"Thank you for installing URY App!\", fg=\"green\")\n\n        \n    except:\n        pass\n     \n"
  },
  {
    "path": "ury/modules.txt",
    "content": "URY"
  },
  {
    "path": "ury/patches/v2_0/default_permissions.py",
    "content": "import frappe\nfrom frappe.permissions import add_permission, update_permission_property\nfrom frappe.core.doctype.doctype.doctype import validate_permissions_for_doctype\n\n\nPERMISSION_KEYS = {\n\t\"select\", \"read\", \"write\", \"create\", \"delete\",\n\t\"submit\", \"cancel\", \"amend\", \"print\", \"email\",\n\t\"report\", \"import\", \"export\", \"share\"\n}\n\ndef execute():\n\tprint(\"Configuring ury role permissions...\")\n\t\n\tpermissions_map = {\n\t\t\"URY Captain\": [\n\t\t\t(\"Account\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Accounts Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Bin\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"BOM\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Branch\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Company\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Cost Center\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Currency\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Customer\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Customer Group\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Item\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Item Price\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Mode of Payment\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"POS Invoice\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"print\": 1}),\n\t\t\t(\"POS Opening Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"POS Profile\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Price List\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Product Bundle\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Quality Goal\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Quality Review\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"create\": 1}),\n\t\t\t(\"Sales Taxes and Charges Template\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Stock Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Territory\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"UOM\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"User\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"User\", {\"permlevel\": 1, \"read\": 1, \"write\": 1}),\n\t\t\t(\"Warehouse\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t],\n\n\t\t\"URY Cashier\": [\n\t\t\t(\"Account\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Accounts Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Bin\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"BOM\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Branch\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Buying Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Company\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Cost Center\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Currency\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Customer\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Customer Group\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Fiscal Year\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Item\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Item Group\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Item Price\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Mode of Payment\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"POS Closing Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1}),\n\t\t\t(\"POS Invoice\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1}),\n\t\t\t(\"POS Invoice Merge Log\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1}),\n\t\t\t(\"POS Opening Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1}),\n\t\t\t(\"POS Profile\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Price List\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Print Format\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Product Bundle\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Quality Goal\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Quality Review\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"create\": 1}),\n\t\t\t(\"Role\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Sales Invoice\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1}),\n\t\t\t(\"Sales Taxes and Charges Template\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Stock Ledger Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Stock Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Tax Rule\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Territory\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"UOM\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"User\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"User\", {\"permlevel\": 1, \"read\": 1, \"write\": 1}),\n\t\t\t(\"Warehouse\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t],\n\n\t\t\"URY Manager\": [\n\t\t\t(\"Account\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Accounts Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Address\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1,}),\n\t\t\t(\"Address Template\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Bank Reconciliation Tool\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Bin\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"BOM\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1}),\n\t\t\t(\"Branch\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Buying Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Company\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Cost Center\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Currency\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Customer\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Customer Group\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Employee\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"GL Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Item\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Item Group\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1,  \"report\": 1}),\n\t\t\t(\"Item Price\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Journal Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"report\": 1}),\n\t\t\t(\"Landed Cost Voucher\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"report\": 1}),\n\t\t\t(\"Material Request\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Mode of Payment\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Module Profile\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Payment Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Payment Ledger Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Payment Term\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Payment Terms Template\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"POS Closing Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1}),\n\t\t\t(\"POS Closing Entry\", {\"permlevel\": 1, \"read\": 1, \"write\": 1}),\n\t\t\t(\"POS Invoice\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"POS Invoice Merge Log\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"cancel\": 1}),\n\t\t\t(\"POS Opening Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1}),\n\t\t\t(\"POS Profile\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Prepared Report\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Price List\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Production Plan\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"report\": 1}),\n\t\t\t(\"Product Bundle\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Purchase Invoice\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Purchase Order\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Purchase Receipt\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Quality Goal\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Quality Review\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Role\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Role Profile\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Sales Invoice\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Sales Order\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Sales Taxes and Charges Template\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Stock Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"report\": 1}),\n\t\t\t(\"Stock Entry Type\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Stock Ledger Entry\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Stock Reconciliation\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"print\": 1, \"report\": 1}),\n\t\t\t(\"Stock Settings\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"Supplier\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"report\": 1}),\n\t\t\t(\"Supplier Group\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Tax Category\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Territory\", {\"permlevel\": 0, \"select\": 1, \"read\": 1}),\n\t\t\t(\"UOM\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"User\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"User\", {\"permlevel\": 1, \"read\": 1}),\n\t\t\t(\"Warehouse\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1}),\n\t\t\t(\"Work Order\", {\"permlevel\": 0, \"select\": 1, \"read\": 1, \"write\": 1, \"create\": 1, \"submit\": 1, \"cancel\": 1, \"report\": 1}),\n\t\t],\n\t}\n\n\tfor role, doctypes in permissions_map.items():\n\t\tfor doctype, perms in doctypes:\n\t\t\tapply_permissions(doctype, role, perms)\n\n\tfrappe.clear_cache()\n\ndef apply_permissions(doctype, role, perms):\n\tpermlevel = perms.get(\"permlevel\", 0)\n\n\tadd_permission(doctype, role, permlevel)\n\n\tfor ptype, value in perms.items():\n\t\tif ptype in PERMISSION_KEYS:\n\t\t\tupdate_permission_property(\n\t\t\t\tdoctype=doctype,\n\t\t\t\trole=role,\n\t\t\t\tpermlevel=permlevel,\n\t\t\t\tptype=ptype,\n\t\t\t\tvalue=value,\n\t\t\t\tvalidate=False\n\t\t\t)\n\n\tvalidate_permissions_for_doctype(doctype)"
  },
  {
    "path": "ury/patches.txt",
    "content": "[pre_model_sync]\n# Patches added in this section will be executed before doctypes are migrated\n\n[post_model_sync]\n# Patches added in this section will be executed after doctypes are migrated\nury.patches.v2_0.default_permissions"
  },
  {
    "path": "ury/permission.py",
    "content": "import frappe\n\ndef check_app_permission():\n\tif frappe.session.user == \"Administrator\" or frappe.session.user == \"System Manager\" :\n\t\treturn True"
  },
  {
    "path": "ury/public/.gitkeep",
    "content": ""
  },
  {
    "path": "ury/public/js/jsrsasign-all-min.js",
    "content": "/*\n * jsrsasign(all) 8.0.4 (2017-09-14) (c) 2010-2017 Kenji Urushima | kjur.github.com/jsrsasign/license\n */\n\n/*!\nCopyright (c) 2011, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 2.9.0\n*/\nif (YAHOO === undefined) { var YAHOO = {} } YAHOO.lang = { extend: function (g, h, f) { if (!h || !g) { throw new Error(\"YAHOO.lang.extend failed, please check that all dependencies are included.\") } var d = function () { }; d.prototype = h.prototype; g.prototype = new d(); g.prototype.constructor = g; g.superclass = h.prototype; if (h.prototype.constructor == Object.prototype.constructor) { h.prototype.constructor = h } if (f) { var b; for (b in f) { g.prototype[b] = f[b] } var e = function () { }, c = [\"toString\", \"valueOf\"]; try { if (/MSIE/.test(navigator.userAgent)) { e = function (j, i) { for (b = 0; b < c.length; b = b + 1) { var l = c[b], k = i[l]; if (typeof k === \"function\" && k != Object.prototype[l]) { j[l] = k } } } } } catch (a) { } e(g.prototype, f) } } };\n\n/*! CryptoJS v3.1.2 core-fix.js\n * code.google.com/p/crypto-js\n * (c) 2009-2013 by Jeff Mott. All rights reserved.\n * code.google.com/p/crypto-js/wiki/License\n * THIS IS FIX of 'core.js' to fix Hmac issue.\n * https://code.google.com/p/crypto-js/issues/detail?id=84\n * https://crypto-js.googlecode.com/svn-history/r667/branches/3.x/src/core.js\n */\nvar CryptoJS = CryptoJS || (function (e, g) { var a = {}; var b = a.lib = {}; var j = b.Base = (function () { function n() { } return { extend: function (p) { n.prototype = this; var o = new n(); if (p) { o.mixIn(p) } if (!o.hasOwnProperty(\"init\")) { o.init = function () { o.$super.init.apply(this, arguments) } } o.init.prototype = o; o.$super = this; return o }, create: function () { var o = this.extend(); o.init.apply(o, arguments); return o }, init: function () { }, mixIn: function (p) { for (var o in p) { if (p.hasOwnProperty(o)) { this[o] = p[o] } } if (p.hasOwnProperty(\"toString\")) { this.toString = p.toString } }, clone: function () { return this.init.prototype.extend(this) } } }()); var l = b.WordArray = j.extend({ init: function (o, n) { o = this.words = o || []; if (n != g) { this.sigBytes = n } else { this.sigBytes = o.length * 4 } }, toString: function (n) { return (n || h).stringify(this) }, concat: function (t) { var q = this.words; var p = t.words; var n = this.sigBytes; var s = t.sigBytes; this.clamp(); if (n % 4) { for (var r = 0; r < s; r++) { var o = (p[r >>> 2] >>> (24 - (r % 4) * 8)) & 255; q[(n + r) >>> 2] |= o << (24 - ((n + r) % 4) * 8) } } else { for (var r = 0; r < s; r += 4) { q[(n + r) >>> 2] = p[r >>> 2] } } this.sigBytes += s; return this }, clamp: function () { var o = this.words; var n = this.sigBytes; o[n >>> 2] &= 4294967295 << (32 - (n % 4) * 8); o.length = e.ceil(n / 4) }, clone: function () { var n = j.clone.call(this); n.words = this.words.slice(0); return n }, random: function (p) { var o = []; for (var n = 0; n < p; n += 4) { o.push((e.random() * 4294967296) | 0) } return new l.init(o, p) } }); var m = a.enc = {}; var h = m.Hex = { stringify: function (p) { var r = p.words; var o = p.sigBytes; var q = []; for (var n = 0; n < o; n++) { var s = (r[n >>> 2] >>> (24 - (n % 4) * 8)) & 255; q.push((s >>> 4).toString(16)); q.push((s & 15).toString(16)) } return q.join(\"\") }, parse: function (p) { var n = p.length; var q = []; for (var o = 0; o < n; o += 2) { q[o >>> 3] |= parseInt(p.substr(o, 2), 16) << (24 - (o % 8) * 4) } return new l.init(q, n / 2) } }; var d = m.Latin1 = { stringify: function (q) { var r = q.words; var p = q.sigBytes; var n = []; for (var o = 0; o < p; o++) { var s = (r[o >>> 2] >>> (24 - (o % 4) * 8)) & 255; n.push(String.fromCharCode(s)) } return n.join(\"\") }, parse: function (p) { var n = p.length; var q = []; for (var o = 0; o < n; o++) { q[o >>> 2] |= (p.charCodeAt(o) & 255) << (24 - (o % 4) * 8) } return new l.init(q, n) } }; var c = m.Utf8 = { stringify: function (n) { try { return decodeURIComponent(escape(d.stringify(n))) } catch (o) { throw new Error(\"Malformed UTF-8 data\") } }, parse: function (n) { return d.parse(unescape(encodeURIComponent(n))) } }; var i = b.BufferedBlockAlgorithm = j.extend({ reset: function () { this._data = new l.init(); this._nDataBytes = 0 }, _append: function (n) { if (typeof n == \"string\") { n = c.parse(n) } this._data.concat(n); this._nDataBytes += n.sigBytes }, _process: function (w) { var q = this._data; var x = q.words; var n = q.sigBytes; var t = this.blockSize; var v = t * 4; var u = n / v; if (w) { u = e.ceil(u) } else { u = e.max((u | 0) - this._minBufferSize, 0) } var s = u * t; var r = e.min(s * 4, n); if (s) { for (var p = 0; p < s; p += t) { this._doProcessBlock(x, p) } var o = x.splice(0, s); q.sigBytes -= r } return new l.init(o, r) }, clone: function () { var n = j.clone.call(this); n._data = this._data.clone(); return n }, _minBufferSize: 0 }); var f = b.Hasher = i.extend({ cfg: j.extend(), init: function (n) { this.cfg = this.cfg.extend(n); this.reset() }, reset: function () { i.reset.call(this); this._doReset() }, update: function (n) { this._append(n); this._process(); return this }, finalize: function (n) { if (n) { this._append(n) } var o = this._doFinalize(); return o }, blockSize: 512 / 32, _createHelper: function (n) { return function (p, o) { return new n.init(o).finalize(p) } }, _createHmacHelper: function (n) { return function (p, o) { return new k.HMAC.init(n, o).finalize(p) } } }); var k = a.algo = {}; return a }(Math));\n/*\nCryptoJS v3.1.2 x64-core-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function (g) { var a = CryptoJS, f = a.lib, e = f.Base, h = f.WordArray, a = a.x64 = {}; a.Word = e.extend({ init: function (b, c) { this.high = b; this.low = c } }); a.WordArray = e.extend({ init: function (b, c) { b = this.words = b || []; this.sigBytes = c != g ? c : 8 * b.length }, toX32: function () { for (var b = this.words, c = b.length, a = [], d = 0; d < c; d++) { var e = b[d]; a.push(e.high); a.push(e.low) } return h.create(a, this.sigBytes) }, clone: function () { for (var b = e.clone.call(this), c = b.words = this.words.slice(0), a = c.length, d = 0; d < a; d++)c[d] = c[d].clone(); return b } }) })();\n\n/*\nCryptoJS v3.1.2 cipher-core.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\nCryptoJS.lib.Cipher || function (u) {\n    var g = CryptoJS, f = g.lib, k = f.Base, l = f.WordArray, q = f.BufferedBlockAlgorithm, r = g.enc.Base64, v = g.algo.EvpKDF, n = f.Cipher = q.extend({\n        cfg: k.extend(), createEncryptor: function (a, b) { return this.create(this._ENC_XFORM_MODE, a, b) }, createDecryptor: function (a, b) { return this.create(this._DEC_XFORM_MODE, a, b) }, init: function (a, b, c) { this.cfg = this.cfg.extend(c); this._xformMode = a; this._key = b; this.reset() }, reset: function () { q.reset.call(this); this._doReset() }, process: function (a) {\n            this._append(a);\n            return this._process()\n        }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, keySize: 4, ivSize: 4, _ENC_XFORM_MODE: 1, _DEC_XFORM_MODE: 2, _createHelper: function (a) { return { encrypt: function (b, c, d) { return (\"string\" == typeof c ? s : j).encrypt(a, b, c, d) }, decrypt: function (b, c, d) { return (\"string\" == typeof c ? s : j).decrypt(a, b, c, d) } } }\n    }); f.StreamCipher = n.extend({ _doFinalize: function () { return this._process(!0) }, blockSize: 1 }); var m = g.mode = {}, t = function (a, b, c) {\n        var d = this._iv; d ? this._iv = u : d = this._prevBlock; for (var e =\n            0; e < c; e++)a[b + e] ^= d[e]\n    }, h = (f.BlockCipherMode = k.extend({ createEncryptor: function (a, b) { return this.Encryptor.create(a, b) }, createDecryptor: function (a, b) { return this.Decryptor.create(a, b) }, init: function (a, b) { this._cipher = a; this._iv = b } })).extend(); h.Encryptor = h.extend({ processBlock: function (a, b) { var c = this._cipher, d = c.blockSize; t.call(this, a, b, d); c.encryptBlock(a, b); this._prevBlock = a.slice(b, b + d) } }); h.Decryptor = h.extend({\n        processBlock: function (a, b) {\n            var c = this._cipher, d = c.blockSize, e = a.slice(b, b + d); c.decryptBlock(a,\n                b); t.call(this, a, b, d); this._prevBlock = e\n        }\n    }); m = m.CBC = h; h = (g.pad = {}).Pkcs7 = { pad: function (a, b) { for (var c = 4 * b, c = c - a.sigBytes % c, d = c << 24 | c << 16 | c << 8 | c, e = [], f = 0; f < c; f += 4)e.push(d); c = l.create(e, c); a.concat(c) }, unpad: function (a) { a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255 } }; f.BlockCipher = n.extend({\n        cfg: n.cfg.extend({ mode: m, padding: h }), reset: function () {\n            n.reset.call(this); var a = this.cfg, b = a.iv, a = a.mode; if (this._xformMode == this._ENC_XFORM_MODE) var c = a.createEncryptor; else c = a.createDecryptor, this._minBufferSize = 1;\n            this._mode = c.call(a, this, b && b.words)\n        }, _doProcessBlock: function (a, b) { this._mode.processBlock(a, b) }, _doFinalize: function () { var a = this.cfg.padding; if (this._xformMode == this._ENC_XFORM_MODE) { a.pad(this._data, this.blockSize); var b = this._process(!0) } else b = this._process(!0), a.unpad(b); return b }, blockSize: 4\n    }); var p = f.CipherParams = k.extend({ init: function (a) { this.mixIn(a) }, toString: function (a) { return (a || this.formatter).stringify(this) } }), m = (g.format = {}).OpenSSL = {\n        stringify: function (a) {\n            var b = a.ciphertext; a = a.salt;\n            return (a ? l.create([1398893684, 1701076831]).concat(a).concat(b) : b).toString(r)\n        }, parse: function (a) { a = r.parse(a); var b = a.words; if (1398893684 == b[0] && 1701076831 == b[1]) { var c = l.create(b.slice(2, 4)); b.splice(0, 4); a.sigBytes -= 16 } return p.create({ ciphertext: a, salt: c }) }\n    }, j = f.SerializableCipher = k.extend({\n        cfg: k.extend({ format: m }), encrypt: function (a, b, c, d) {\n            d = this.cfg.extend(d); var e = a.createEncryptor(c, d); b = e.finalize(b); e = e.cfg; return p.create({\n                ciphertext: b, key: c, iv: e.iv, algorithm: a, mode: e.mode, padding: e.padding,\n                blockSize: a.blockSize, formatter: d.format\n            })\n        }, decrypt: function (a, b, c, d) { d = this.cfg.extend(d); b = this._parse(b, d.format); return a.createDecryptor(c, d).finalize(b.ciphertext) }, _parse: function (a, b) { return \"string\" == typeof a ? b.parse(a, this) : a }\n    }), g = (g.kdf = {}).OpenSSL = { execute: function (a, b, c, d) { d || (d = l.random(8)); a = v.create({ keySize: b + c }).compute(a, d); c = l.create(a.words.slice(b), 4 * c); a.sigBytes = 4 * b; return p.create({ key: a, iv: c, salt: d }) } }, s = f.PasswordBasedCipher = j.extend({\n        cfg: j.cfg.extend({ kdf: g }), encrypt: function (a,\n            b, c, d) { d = this.cfg.extend(d); c = d.kdf.execute(c, a.keySize, a.ivSize); d.iv = c.iv; a = j.encrypt.call(this, a, b, c.key, d); a.mixIn(c); return a }, decrypt: function (a, b, c, d) { d = this.cfg.extend(d); b = this._parse(b, d.format); c = d.kdf.execute(c, a.keySize, a.ivSize, b.salt); d.iv = c.iv; return j.decrypt.call(this, a, b, c.key, d) }\n    })\n}();\n\n/*\nCryptoJS v3.1.2 aes.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    for (var q = CryptoJS, x = q.lib.BlockCipher, r = q.algo, j = [], y = [], z = [], A = [], B = [], C = [], s = [], u = [], v = [], w = [], g = [], k = 0; 256 > k; k++)g[k] = 128 > k ? k << 1 : k << 1 ^ 283; for (var n = 0, l = 0, k = 0; 256 > k; k++) { var f = l ^ l << 1 ^ l << 2 ^ l << 3 ^ l << 4, f = f >>> 8 ^ f & 255 ^ 99; j[n] = f; y[f] = n; var t = g[n], D = g[t], E = g[D], b = 257 * g[f] ^ 16843008 * f; z[n] = b << 24 | b >>> 8; A[n] = b << 16 | b >>> 16; B[n] = b << 8 | b >>> 24; C[n] = b; b = 16843009 * E ^ 65537 * D ^ 257 * t ^ 16843008 * n; s[f] = b << 24 | b >>> 8; u[f] = b << 16 | b >>> 16; v[f] = b << 8 | b >>> 24; w[f] = b; n ? (n = t ^ g[g[g[E ^ t]]], l ^= g[g[l]]) : n = l = 1 } var F = [0, 1, 2, 4, 8,\n        16, 32, 64, 128, 27, 54], r = r.AES = x.extend({\n            _doReset: function () {\n                for (var c = this._key, e = c.words, a = c.sigBytes / 4, c = 4 * ((this._nRounds = a + 6) + 1), b = this._keySchedule = [], h = 0; h < c; h++)if (h < a) b[h] = e[h]; else { var d = b[h - 1]; h % a ? 6 < a && 4 == h % a && (d = j[d >>> 24] << 24 | j[d >>> 16 & 255] << 16 | j[d >>> 8 & 255] << 8 | j[d & 255]) : (d = d << 8 | d >>> 24, d = j[d >>> 24] << 24 | j[d >>> 16 & 255] << 16 | j[d >>> 8 & 255] << 8 | j[d & 255], d ^= F[h / a | 0] << 24); b[h] = b[h - a] ^ d } e = this._invKeySchedule = []; for (a = 0; a < c; a++)h = c - a, d = a % 4 ? b[h] : b[h - 4], e[a] = 4 > a || 4 >= h ? d : s[j[d >>> 24]] ^ u[j[d >>> 16 & 255]] ^ v[j[d >>>\n                    8 & 255]] ^ w[j[d & 255]]\n            }, encryptBlock: function (c, e) { this._doCryptBlock(c, e, this._keySchedule, z, A, B, C, j) }, decryptBlock: function (c, e) { var a = c[e + 1]; c[e + 1] = c[e + 3]; c[e + 3] = a; this._doCryptBlock(c, e, this._invKeySchedule, s, u, v, w, y); a = c[e + 1]; c[e + 1] = c[e + 3]; c[e + 3] = a }, _doCryptBlock: function (c, e, a, b, h, d, j, m) {\n                for (var n = this._nRounds, f = c[e] ^ a[0], g = c[e + 1] ^ a[1], k = c[e + 2] ^ a[2], p = c[e + 3] ^ a[3], l = 4, t = 1; t < n; t++)var q = b[f >>> 24] ^ h[g >>> 16 & 255] ^ d[k >>> 8 & 255] ^ j[p & 255] ^ a[l++], r = b[g >>> 24] ^ h[k >>> 16 & 255] ^ d[p >>> 8 & 255] ^ j[f & 255] ^ a[l++], s =\n                    b[k >>> 24] ^ h[p >>> 16 & 255] ^ d[f >>> 8 & 255] ^ j[g & 255] ^ a[l++], p = b[p >>> 24] ^ h[f >>> 16 & 255] ^ d[g >>> 8 & 255] ^ j[k & 255] ^ a[l++], f = q, g = r, k = s; q = (m[f >>> 24] << 24 | m[g >>> 16 & 255] << 16 | m[k >>> 8 & 255] << 8 | m[p & 255]) ^ a[l++]; r = (m[g >>> 24] << 24 | m[k >>> 16 & 255] << 16 | m[p >>> 8 & 255] << 8 | m[f & 255]) ^ a[l++]; s = (m[k >>> 24] << 24 | m[p >>> 16 & 255] << 16 | m[f >>> 8 & 255] << 8 | m[g & 255]) ^ a[l++]; p = (m[p >>> 24] << 24 | m[f >>> 16 & 255] << 16 | m[g >>> 8 & 255] << 8 | m[k & 255]) ^ a[l++]; c[e] = q; c[e + 1] = r; c[e + 2] = s; c[e + 3] = p\n            }, keySize: 8\n        }); q.AES = x._createHelper(r)\n})();\n\n/*\nCryptoJS v3.1.2 tripledes-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    function j(b, c) { var a = (this._lBlock >>> b ^ this._rBlock) & c; this._rBlock ^= a; this._lBlock ^= a << b } function l(b, c) { var a = (this._rBlock >>> b ^ this._lBlock) & c; this._lBlock ^= a; this._rBlock ^= a << b } var h = CryptoJS, e = h.lib, n = e.WordArray, e = e.BlockCipher, g = h.algo, q = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4], p = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47,\n        55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32], r = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28], s = [{\n            \"0\": 8421888, 268435456: 32768, 536870912: 8421378, 805306368: 2, 1073741824: 512, 1342177280: 8421890, 1610612736: 8389122, 1879048192: 8388608, 2147483648: 514, 2415919104: 8389120, 2684354560: 33280, 2952790016: 8421376, 3221225472: 32770, 3489660928: 8388610, 3758096384: 0, 4026531840: 33282, 134217728: 0, 402653184: 8421890, 671088640: 33282, 939524096: 32768, 1207959552: 8421888, 1476395008: 512, 1744830464: 8421378, 2013265920: 2,\n            2281701376: 8389120, 2550136832: 33280, 2818572288: 8421376, 3087007744: 8389122, 3355443200: 8388610, 3623878656: 32770, 3892314112: 514, 4160749568: 8388608, 1: 32768, 268435457: 2, 536870913: 8421888, 805306369: 8388608, 1073741825: 8421378, 1342177281: 33280, 1610612737: 512, 1879048193: 8389122, 2147483649: 8421890, 2415919105: 8421376, 2684354561: 8388610, 2952790017: 33282, 3221225473: 514, 3489660929: 8389120, 3758096385: 32770, 4026531841: 0, 134217729: 8421890, 402653185: 8421376, 671088641: 8388608, 939524097: 512, 1207959553: 32768, 1476395009: 8388610,\n            1744830465: 2, 2013265921: 33282, 2281701377: 32770, 2550136833: 8389122, 2818572289: 514, 3087007745: 8421888, 3355443201: 8389120, 3623878657: 0, 3892314113: 33280, 4160749569: 8421378\n        }, {\n            \"0\": 1074282512, 16777216: 16384, 33554432: 524288, 50331648: 1074266128, 67108864: 1073741840, 83886080: 1074282496, 100663296: 1073758208, 117440512: 16, 134217728: 540672, 150994944: 1073758224, 167772160: 1073741824, 184549376: 540688, 201326592: 524304, 218103808: 0, 234881024: 16400, 251658240: 1074266112, 8388608: 1073758208, 25165824: 540688, 41943040: 16, 58720256: 1073758224,\n            75497472: 1074282512, 92274688: 1073741824, 109051904: 524288, 125829120: 1074266128, 142606336: 524304, 159383552: 0, 176160768: 16384, 192937984: 1074266112, 209715200: 1073741840, 226492416: 540672, 243269632: 1074282496, 260046848: 16400, 268435456: 0, 285212672: 1074266128, 301989888: 1073758224, 318767104: 1074282496, 335544320: 1074266112, 352321536: 16, 369098752: 540688, 385875968: 16384, 402653184: 16400, 419430400: 524288, 436207616: 524304, 452984832: 1073741840, 469762048: 540672, 486539264: 1073758208, 503316480: 1073741824, 520093696: 1074282512,\n            276824064: 540688, 293601280: 524288, 310378496: 1074266112, 327155712: 16384, 343932928: 1073758208, 360710144: 1074282512, 377487360: 16, 394264576: 1073741824, 411041792: 1074282496, 427819008: 1073741840, 444596224: 1073758224, 461373440: 524304, 478150656: 0, 494927872: 16400, 511705088: 1074266128, 528482304: 540672\n        }, {\n            \"0\": 260, 1048576: 0, 2097152: 67109120, 3145728: 65796, 4194304: 65540, 5242880: 67108868, 6291456: 67174660, 7340032: 67174400, 8388608: 67108864, 9437184: 67174656, 10485760: 65792, 11534336: 67174404, 12582912: 67109124, 13631488: 65536,\n            14680064: 4, 15728640: 256, 524288: 67174656, 1572864: 67174404, 2621440: 0, 3670016: 67109120, 4718592: 67108868, 5767168: 65536, 6815744: 65540, 7864320: 260, 8912896: 4, 9961472: 256, 11010048: 67174400, 12058624: 65796, 13107200: 65792, 14155776: 67109124, 15204352: 67174660, 16252928: 67108864, 16777216: 67174656, 17825792: 65540, 18874368: 65536, 19922944: 67109120, 20971520: 256, 22020096: 67174660, 23068672: 67108868, 24117248: 0, 25165824: 67109124, 26214400: 67108864, 27262976: 4, 28311552: 65792, 29360128: 67174400, 30408704: 260, 31457280: 65796, 32505856: 67174404,\n            17301504: 67108864, 18350080: 260, 19398656: 67174656, 20447232: 0, 21495808: 65540, 22544384: 67109120, 23592960: 256, 24641536: 67174404, 25690112: 65536, 26738688: 67174660, 27787264: 65796, 28835840: 67108868, 29884416: 67109124, 30932992: 67174400, 31981568: 4, 33030144: 65792\n        }, {\n            \"0\": 2151682048, 65536: 2147487808, 131072: 4198464, 196608: 2151677952, 262144: 0, 327680: 4198400, 393216: 2147483712, 458752: 4194368, 524288: 2147483648, 589824: 4194304, 655360: 64, 720896: 2147487744, 786432: 2151678016, 851968: 4160, 917504: 4096, 983040: 2151682112, 32768: 2147487808,\n            98304: 64, 163840: 2151678016, 229376: 2147487744, 294912: 4198400, 360448: 2151682112, 425984: 0, 491520: 2151677952, 557056: 4096, 622592: 2151682048, 688128: 4194304, 753664: 4160, 819200: 2147483648, 884736: 4194368, 950272: 4198464, 1015808: 2147483712, 1048576: 4194368, 1114112: 4198400, 1179648: 2147483712, 1245184: 0, 1310720: 4160, 1376256: 2151678016, 1441792: 2151682048, 1507328: 2147487808, 1572864: 2151682112, 1638400: 2147483648, 1703936: 2151677952, 1769472: 4198464, 1835008: 2147487744, 1900544: 4194304, 1966080: 64, 2031616: 4096, 1081344: 2151677952,\n            1146880: 2151682112, 1212416: 0, 1277952: 4198400, 1343488: 4194368, 1409024: 2147483648, 1474560: 2147487808, 1540096: 64, 1605632: 2147483712, 1671168: 4096, 1736704: 2147487744, 1802240: 2151678016, 1867776: 4160, 1933312: 2151682048, 1998848: 4194304, 2064384: 4198464\n        }, {\n            \"0\": 128, 4096: 17039360, 8192: 262144, 12288: 536870912, 16384: 537133184, 20480: 16777344, 24576: 553648256, 28672: 262272, 32768: 16777216, 36864: 537133056, 40960: 536871040, 45056: 553910400, 49152: 553910272, 53248: 0, 57344: 17039488, 61440: 553648128, 2048: 17039488, 6144: 553648256,\n            10240: 128, 14336: 17039360, 18432: 262144, 22528: 537133184, 26624: 553910272, 30720: 536870912, 34816: 537133056, 38912: 0, 43008: 553910400, 47104: 16777344, 51200: 536871040, 55296: 553648128, 59392: 16777216, 63488: 262272, 65536: 262144, 69632: 128, 73728: 536870912, 77824: 553648256, 81920: 16777344, 86016: 553910272, 90112: 537133184, 94208: 16777216, 98304: 553910400, 102400: 553648128, 106496: 17039360, 110592: 537133056, 114688: 262272, 118784: 536871040, 122880: 0, 126976: 17039488, 67584: 553648256, 71680: 16777216, 75776: 17039360, 79872: 537133184,\n            83968: 536870912, 88064: 17039488, 92160: 128, 96256: 553910272, 100352: 262272, 104448: 553910400, 108544: 0, 112640: 553648128, 116736: 16777344, 120832: 262144, 124928: 537133056, 129024: 536871040\n        }, {\n            \"0\": 268435464, 256: 8192, 512: 270532608, 768: 270540808, 1024: 268443648, 1280: 2097152, 1536: 2097160, 1792: 268435456, 2048: 0, 2304: 268443656, 2560: 2105344, 2816: 8, 3072: 270532616, 3328: 2105352, 3584: 8200, 3840: 270540800, 128: 270532608, 384: 270540808, 640: 8, 896: 2097152, 1152: 2105352, 1408: 268435464, 1664: 268443648, 1920: 8200, 2176: 2097160, 2432: 8192,\n            2688: 268443656, 2944: 270532616, 3200: 0, 3456: 270540800, 3712: 2105344, 3968: 268435456, 4096: 268443648, 4352: 270532616, 4608: 270540808, 4864: 8200, 5120: 2097152, 5376: 268435456, 5632: 268435464, 5888: 2105344, 6144: 2105352, 6400: 0, 6656: 8, 6912: 270532608, 7168: 8192, 7424: 268443656, 7680: 270540800, 7936: 2097160, 4224: 8, 4480: 2105344, 4736: 2097152, 4992: 268435464, 5248: 268443648, 5504: 8200, 5760: 270540808, 6016: 270532608, 6272: 270540800, 6528: 270532616, 6784: 8192, 7040: 2105352, 7296: 2097160, 7552: 0, 7808: 268435456, 8064: 268443656\n        }, {\n            \"0\": 1048576,\n            16: 33555457, 32: 1024, 48: 1049601, 64: 34604033, 80: 0, 96: 1, 112: 34603009, 128: 33555456, 144: 1048577, 160: 33554433, 176: 34604032, 192: 34603008, 208: 1025, 224: 1049600, 240: 33554432, 8: 34603009, 24: 0, 40: 33555457, 56: 34604032, 72: 1048576, 88: 33554433, 104: 33554432, 120: 1025, 136: 1049601, 152: 33555456, 168: 34603008, 184: 1048577, 200: 1024, 216: 34604033, 232: 1, 248: 1049600, 256: 33554432, 272: 1048576, 288: 33555457, 304: 34603009, 320: 1048577, 336: 33555456, 352: 34604032, 368: 1049601, 384: 1025, 400: 34604033, 416: 1049600, 432: 1, 448: 0, 464: 34603008, 480: 33554433,\n            496: 1024, 264: 1049600, 280: 33555457, 296: 34603009, 312: 1, 328: 33554432, 344: 1048576, 360: 1025, 376: 34604032, 392: 33554433, 408: 34603008, 424: 0, 440: 34604033, 456: 1049601, 472: 1024, 488: 33555456, 504: 1048577\n        }, {\n            \"0\": 134219808, 1: 131072, 2: 134217728, 3: 32, 4: 131104, 5: 134350880, 6: 134350848, 7: 2048, 8: 134348800, 9: 134219776, 10: 133120, 11: 134348832, 12: 2080, 13: 0, 14: 134217760, 15: 133152, 2147483648: 2048, 2147483649: 134350880, 2147483650: 134219808, 2147483651: 134217728, 2147483652: 134348800, 2147483653: 133120, 2147483654: 133152, 2147483655: 32,\n            2147483656: 134217760, 2147483657: 2080, 2147483658: 131104, 2147483659: 134350848, 2147483660: 0, 2147483661: 134348832, 2147483662: 134219776, 2147483663: 131072, 16: 133152, 17: 134350848, 18: 32, 19: 2048, 20: 134219776, 21: 134217760, 22: 134348832, 23: 131072, 24: 0, 25: 131104, 26: 134348800, 27: 134219808, 28: 134350880, 29: 133120, 30: 2080, 31: 134217728, 2147483664: 131072, 2147483665: 2048, 2147483666: 134348832, 2147483667: 133152, 2147483668: 32, 2147483669: 134348800, 2147483670: 134217728, 2147483671: 134219808, 2147483672: 134350880, 2147483673: 134217760,\n            2147483674: 134219776, 2147483675: 0, 2147483676: 133120, 2147483677: 2080, 2147483678: 131104, 2147483679: 134350848\n        }], t = [4160749569, 528482304, 33030144, 2064384, 129024, 8064, 504, 2147483679], m = g.DES = e.extend({\n            _doReset: function () {\n                for (var b = this._key.words, c = [], a = 0; 56 > a; a++) { var f = q[a] - 1; c[a] = b[f >>> 5] >>> 31 - f % 32 & 1 } b = this._subKeys = []; for (f = 0; 16 > f; f++) {\n                    for (var d = b[f] = [], e = r[f], a = 0; 24 > a; a++)d[a / 6 | 0] |= c[(p[a] - 1 + e) % 28] << 31 - a % 6, d[4 + (a / 6 | 0)] |= c[28 + (p[a + 24] - 1 + e) % 28] << 31 - a % 6; d[0] = d[0] << 1 | d[0] >>> 31; for (a = 1; 7 > a; a++)d[a] >>>=\n                        4 * (a - 1) + 3; d[7] = d[7] << 5 | d[7] >>> 27\n                } c = this._invSubKeys = []; for (a = 0; 16 > a; a++)c[a] = b[15 - a]\n            }, encryptBlock: function (b, c) { this._doCryptBlock(b, c, this._subKeys) }, decryptBlock: function (b, c) { this._doCryptBlock(b, c, this._invSubKeys) }, _doCryptBlock: function (b, c, a) {\n                this._lBlock = b[c]; this._rBlock = b[c + 1]; j.call(this, 4, 252645135); j.call(this, 16, 65535); l.call(this, 2, 858993459); l.call(this, 8, 16711935); j.call(this, 1, 1431655765); for (var f = 0; 16 > f; f++) {\n                    for (var d = a[f], e = this._lBlock, h = this._rBlock, g = 0, k = 0; 8 > k; k++)g |= s[k][((h ^\n                        d[k]) & t[k]) >>> 0]; this._lBlock = h; this._rBlock = e ^ g\n                } a = this._lBlock; this._lBlock = this._rBlock; this._rBlock = a; j.call(this, 1, 1431655765); l.call(this, 8, 16711935); l.call(this, 2, 858993459); j.call(this, 16, 65535); j.call(this, 4, 252645135); b[c] = this._lBlock; b[c + 1] = this._rBlock\n            }, keySize: 2, ivSize: 2, blockSize: 2\n        }); h.DES = e._createHelper(m); g = g.TripleDES = e.extend({\n            _doReset: function () {\n                var b = this._key.words; this._des1 = m.createEncryptor(n.create(b.slice(0, 2))); this._des2 = m.createEncryptor(n.create(b.slice(2, 4))); this._des3 =\n                    m.createEncryptor(n.create(b.slice(4, 6)))\n            }, encryptBlock: function (b, c) { this._des1.encryptBlock(b, c); this._des2.decryptBlock(b, c); this._des3.encryptBlock(b, c) }, decryptBlock: function (b, c) { this._des3.decryptBlock(b, c); this._des2.encryptBlock(b, c); this._des1.decryptBlock(b, c) }, keySize: 6, ivSize: 2, blockSize: 2\n        }); h.TripleDES = e._createHelper(g)\n})();\n\n/*\nCryptoJS v3.1.2 enc-base64.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    var h = CryptoJS, j = h.lib.WordArray; h.enc.Base64 = {\n        stringify: function (b) { var e = b.words, f = b.sigBytes, c = this._map; b.clamp(); b = []; for (var a = 0; a < f; a += 3)for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++)b.push(c.charAt(d >>> 6 * (3 - g) & 63)); if (e = c.charAt(64)) for (; b.length % 4;)b.push(e); return b.join(\"\") }, parse: function (b) {\n            var e = b.length, f = this._map, c = f.charAt(64); c && (c = b.indexOf(c), -1 != c && (e = c)); for (var c = [], a = 0, d = 0; d <\n                e; d++)if (d % 4) { var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4), h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4); c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4); a++ } return j.create(c, a)\n        }, _map: \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\"\n    }\n})();\n\n/*\nCryptoJS v3.1.2 md5.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function (E) {\n    function h(a, f, g, j, p, h, k) { a = a + (f & g | ~f & j) + p + k; return (a << h | a >>> 32 - h) + f } function k(a, f, g, j, p, h, k) { a = a + (f & j | g & ~j) + p + k; return (a << h | a >>> 32 - h) + f } function l(a, f, g, j, h, k, l) { a = a + (f ^ g ^ j) + h + l; return (a << k | a >>> 32 - k) + f } function n(a, f, g, j, h, k, l) { a = a + (g ^ (f | ~j)) + h + l; return (a << k | a >>> 32 - k) + f } for (var r = CryptoJS, q = r.lib, F = q.WordArray, s = q.Hasher, q = r.algo, a = [], t = 0; 64 > t; t++)a[t] = 4294967296 * E.abs(E.sin(t + 1)) | 0; q = q.MD5 = s.extend({\n        _doReset: function () { this._hash = new F.init([1732584193, 4023233417, 2562383102, 271733878]) },\n        _doProcessBlock: function (m, f) {\n            for (var g = 0; 16 > g; g++) { var j = f + g, p = m[j]; m[j] = (p << 8 | p >>> 24) & 16711935 | (p << 24 | p >>> 8) & 4278255360 } var g = this._hash.words, j = m[f + 0], p = m[f + 1], q = m[f + 2], r = m[f + 3], s = m[f + 4], t = m[f + 5], u = m[f + 6], v = m[f + 7], w = m[f + 8], x = m[f + 9], y = m[f + 10], z = m[f + 11], A = m[f + 12], B = m[f + 13], C = m[f + 14], D = m[f + 15], b = g[0], c = g[1], d = g[2], e = g[3], b = h(b, c, d, e, j, 7, a[0]), e = h(e, b, c, d, p, 12, a[1]), d = h(d, e, b, c, q, 17, a[2]), c = h(c, d, e, b, r, 22, a[3]), b = h(b, c, d, e, s, 7, a[4]), e = h(e, b, c, d, t, 12, a[5]), d = h(d, e, b, c, u, 17, a[6]), c = h(c, d, e, b, v, 22, a[7]),\n                b = h(b, c, d, e, w, 7, a[8]), e = h(e, b, c, d, x, 12, a[9]), d = h(d, e, b, c, y, 17, a[10]), c = h(c, d, e, b, z, 22, a[11]), b = h(b, c, d, e, A, 7, a[12]), e = h(e, b, c, d, B, 12, a[13]), d = h(d, e, b, c, C, 17, a[14]), c = h(c, d, e, b, D, 22, a[15]), b = k(b, c, d, e, p, 5, a[16]), e = k(e, b, c, d, u, 9, a[17]), d = k(d, e, b, c, z, 14, a[18]), c = k(c, d, e, b, j, 20, a[19]), b = k(b, c, d, e, t, 5, a[20]), e = k(e, b, c, d, y, 9, a[21]), d = k(d, e, b, c, D, 14, a[22]), c = k(c, d, e, b, s, 20, a[23]), b = k(b, c, d, e, x, 5, a[24]), e = k(e, b, c, d, C, 9, a[25]), d = k(d, e, b, c, r, 14, a[26]), c = k(c, d, e, b, w, 20, a[27]), b = k(b, c, d, e, B, 5, a[28]), e = k(e, b,\n                    c, d, q, 9, a[29]), d = k(d, e, b, c, v, 14, a[30]), c = k(c, d, e, b, A, 20, a[31]), b = l(b, c, d, e, t, 4, a[32]), e = l(e, b, c, d, w, 11, a[33]), d = l(d, e, b, c, z, 16, a[34]), c = l(c, d, e, b, C, 23, a[35]), b = l(b, c, d, e, p, 4, a[36]), e = l(e, b, c, d, s, 11, a[37]), d = l(d, e, b, c, v, 16, a[38]), c = l(c, d, e, b, y, 23, a[39]), b = l(b, c, d, e, B, 4, a[40]), e = l(e, b, c, d, j, 11, a[41]), d = l(d, e, b, c, r, 16, a[42]), c = l(c, d, e, b, u, 23, a[43]), b = l(b, c, d, e, x, 4, a[44]), e = l(e, b, c, d, A, 11, a[45]), d = l(d, e, b, c, D, 16, a[46]), c = l(c, d, e, b, q, 23, a[47]), b = n(b, c, d, e, j, 6, a[48]), e = n(e, b, c, d, v, 10, a[49]), d = n(d, e, b, c,\n                        C, 15, a[50]), c = n(c, d, e, b, t, 21, a[51]), b = n(b, c, d, e, A, 6, a[52]), e = n(e, b, c, d, r, 10, a[53]), d = n(d, e, b, c, y, 15, a[54]), c = n(c, d, e, b, p, 21, a[55]), b = n(b, c, d, e, w, 6, a[56]), e = n(e, b, c, d, D, 10, a[57]), d = n(d, e, b, c, u, 15, a[58]), c = n(c, d, e, b, B, 21, a[59]), b = n(b, c, d, e, s, 6, a[60]), e = n(e, b, c, d, z, 10, a[61]), d = n(d, e, b, c, q, 15, a[62]), c = n(c, d, e, b, x, 21, a[63]); g[0] = g[0] + b | 0; g[1] = g[1] + c | 0; g[2] = g[2] + d | 0; g[3] = g[3] + e | 0\n        }, _doFinalize: function () {\n            var a = this._data, f = a.words, g = 8 * this._nDataBytes, j = 8 * a.sigBytes; f[j >>> 5] |= 128 << 24 - j % 32; var h = E.floor(g /\n                4294967296); f[(j + 64 >>> 9 << 4) + 15] = (h << 8 | h >>> 24) & 16711935 | (h << 24 | h >>> 8) & 4278255360; f[(j + 64 >>> 9 << 4) + 14] = (g << 8 | g >>> 24) & 16711935 | (g << 24 | g >>> 8) & 4278255360; a.sigBytes = 4 * (f.length + 1); this._process(); a = this._hash; f = a.words; for (g = 0; 4 > g; g++)j = f[g], f[g] = (j << 8 | j >>> 24) & 16711935 | (j << 24 | j >>> 8) & 4278255360; return a\n        }, clone: function () { var a = s.clone.call(this); a._hash = this._hash.clone(); return a }\n    }); r.MD5 = s._createHelper(q); r.HmacMD5 = s._createHmacHelper(q)\n})(Math);\n\n/*\nCryptoJS v3.1.2 sha1-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    var k = CryptoJS, b = k.lib, m = b.WordArray, l = b.Hasher, d = [], b = k.algo.SHA1 = l.extend({\n        _doReset: function () { this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (n, p) {\n            for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) {\n                if (16 > c) d[c] = n[p + c] | 0; else { var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16]; d[c] = g << 1 | g >>> 31 } g = (e << 5 | e >>> 27) + b + d[c]; g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^\n                    j) - 899497514); b = j; j = h; h = f << 30 | f >>> 2; f = e; e = g\n            } a[0] = a[0] + e | 0; a[1] = a[1] + f | 0; a[2] = a[2] + h | 0; a[3] = a[3] + j | 0; a[4] = a[4] + b | 0\n        }, _doFinalize: function () { var b = this._data, d = b.words, a = 8 * this._nDataBytes, e = 8 * b.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296); d[(e + 64 >>> 9 << 4) + 15] = a; b.sigBytes = 4 * d.length; this._process(); return this._hash }, clone: function () { var b = l.clone.call(this); b._hash = this._hash.clone(); return b }\n    }); k.SHA1 = l._createHelper(b); k.HmacSHA1 = l._createHmacHelper(b)\n})();\n\n/*\nCryptoJS v3.1.2 sha256-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function (k) {\n    for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function (q) { return 4294967296 * (q - (q | 0)) | 0 }, l = 2, b = 0; 64 > b;) { var d; a: { d = l; for (var w = k.sqrt(d), r = 2; r <= w; r++)if (!(d % r)) { d = !1; break a } d = !0 } d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++); l++ } var n = [], h = h.SHA256 = j.extend({\n        _doReset: function () { this._hash = new v.init(s.slice(0)) }, _doProcessBlock: function (q, h) {\n            for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) {\n                if (16 > e) n[e] =\n                    q[h + e] | 0; else { var m = n[e - 15], p = n[e - 2]; n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16] } m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e]; p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b); l = j; j = g; g = f; f = k + m | 0; k = b; b = d; d = c; c = m + p | 0\n            } a[0] = a[0] + c | 0; a[1] = a[1] + d | 0; a[2] = a[2] + b | 0; a[3] = a[3] + k | 0; a[4] = a[4] + f | 0; a[5] = a[5] + g | 0; a[6] = a[6] + j | 0; a[7] = a[7] + l | 0\n        }, _doFinalize: function () {\n            var d = this._data, b = d.words, a = 8 * this._nDataBytes, c = 8 * d.sigBytes;\n            b[c >>> 5] |= 128 << 24 - c % 32; b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296); b[(c + 64 >>> 9 << 4) + 15] = a; d.sigBytes = 4 * b.length; this._process(); return this._hash\n        }, clone: function () { var b = j.clone.call(this); b._hash = this._hash.clone(); return b }\n    }); g.SHA256 = j._createHelper(h); g.HmacSHA256 = j._createHmacHelper(h)\n})(Math);\n\n/*\nCryptoJS v3.1.2 sha224-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () { var b = CryptoJS, d = b.lib.WordArray, a = b.algo, c = a.SHA256, a = a.SHA224 = c.extend({ _doReset: function () { this._hash = new d.init([3238371032, 914150663, 812702999, 4144912697, 4290775857, 1750603025, 1694076839, 3204075428]) }, _doFinalize: function () { var a = c._doFinalize.call(this); a.sigBytes -= 4; return a } }); b.SHA224 = c._createHelper(a); b.HmacSHA224 = c._createHmacHelper(a) })();\n\n/*\nCryptoJS v3.1.2 sha512-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    function a() { return d.create.apply(d, arguments) } for (var n = CryptoJS, r = n.lib.Hasher, e = n.x64, d = e.Word, T = e.WordArray, e = n.algo, ea = [a(1116352408, 3609767458), a(1899447441, 602891725), a(3049323471, 3964484399), a(3921009573, 2173295548), a(961987163, 4081628472), a(1508970993, 3053834265), a(2453635748, 2937671579), a(2870763221, 3664609560), a(3624381080, 2734883394), a(310598401, 1164996542), a(607225278, 1323610764), a(1426881987, 3590304994), a(1925078388, 4068182383), a(2162078206, 991336113), a(2614888103, 633803317),\n    a(3248222580, 3479774868), a(3835390401, 2666613458), a(4022224774, 944711139), a(264347078, 2341262773), a(604807628, 2007800933), a(770255983, 1495990901), a(1249150122, 1856431235), a(1555081692, 3175218132), a(1996064986, 2198950837), a(2554220882, 3999719339), a(2821834349, 766784016), a(2952996808, 2566594879), a(3210313671, 3203337956), a(3336571891, 1034457026), a(3584528711, 2466948901), a(113926993, 3758326383), a(338241895, 168717936), a(666307205, 1188179964), a(773529912, 1546045734), a(1294757372, 1522805485), a(1396182291,\n        2643833823), a(1695183700, 2343527390), a(1986661051, 1014477480), a(2177026350, 1206759142), a(2456956037, 344077627), a(2730485921, 1290863460), a(2820302411, 3158454273), a(3259730800, 3505952657), a(3345764771, 106217008), a(3516065817, 3606008344), a(3600352804, 1432725776), a(4094571909, 1467031594), a(275423344, 851169720), a(430227734, 3100823752), a(506948616, 1363258195), a(659060556, 3750685593), a(883997877, 3785050280), a(958139571, 3318307427), a(1322822218, 3812723403), a(1537002063, 2003034995), a(1747873779, 3602036899),\n    a(1955562222, 1575990012), a(2024104815, 1125592928), a(2227730452, 2716904306), a(2361852424, 442776044), a(2428436474, 593698344), a(2756734187, 3733110249), a(3204031479, 2999351573), a(3329325298, 3815920427), a(3391569614, 3928383900), a(3515267271, 566280711), a(3940187606, 3454069534), a(4118630271, 4000239992), a(116418474, 1914138554), a(174292421, 2731055270), a(289380356, 3203993006), a(460393269, 320620315), a(685471733, 587496836), a(852142971, 1086792851), a(1017036298, 365543100), a(1126000580, 2618297676), a(1288033470,\n        3409855158), a(1501505948, 4234509866), a(1607167915, 987167468), a(1816402316, 1246189591)], v = [], w = 0; 80 > w; w++)v[w] = a(); e = e.SHA512 = r.extend({\n            _doReset: function () { this._hash = new T.init([new d.init(1779033703, 4089235720), new d.init(3144134277, 2227873595), new d.init(1013904242, 4271175723), new d.init(2773480762, 1595750129), new d.init(1359893119, 2917565137), new d.init(2600822924, 725511199), new d.init(528734635, 4215389547), new d.init(1541459225, 327033209)]) }, _doProcessBlock: function (a, d) {\n                for (var f = this._hash.words,\n                    F = f[0], e = f[1], n = f[2], r = f[3], G = f[4], H = f[5], I = f[6], f = f[7], w = F.high, J = F.low, X = e.high, K = e.low, Y = n.high, L = n.low, Z = r.high, M = r.low, $ = G.high, N = G.low, aa = H.high, O = H.low, ba = I.high, P = I.low, ca = f.high, Q = f.low, k = w, g = J, z = X, x = K, A = Y, y = L, U = Z, B = M, l = $, h = N, R = aa, C = O, S = ba, D = P, V = ca, E = Q, m = 0; 80 > m; m++) {\n                        var s = v[m]; if (16 > m) var j = s.high = a[d + 2 * m] | 0, b = s.low = a[d + 2 * m + 1] | 0; else {\n                            var j = v[m - 15], b = j.high, p = j.low, j = (b >>> 1 | p << 31) ^ (b >>> 8 | p << 24) ^ b >>> 7, p = (p >>> 1 | b << 31) ^ (p >>> 8 | b << 24) ^ (p >>> 7 | b << 25), u = v[m - 2], b = u.high, c = u.low, u = (b >>> 19 | c << 13) ^ (b <<\n                                3 | c >>> 29) ^ b >>> 6, c = (c >>> 19 | b << 13) ^ (c << 3 | b >>> 29) ^ (c >>> 6 | b << 26), b = v[m - 7], W = b.high, t = v[m - 16], q = t.high, t = t.low, b = p + b.low, j = j + W + (b >>> 0 < p >>> 0 ? 1 : 0), b = b + c, j = j + u + (b >>> 0 < c >>> 0 ? 1 : 0), b = b + t, j = j + q + (b >>> 0 < t >>> 0 ? 1 : 0); s.high = j; s.low = b\n                        } var W = l & R ^ ~l & S, t = h & C ^ ~h & D, s = k & z ^ k & A ^ z & A, T = g & x ^ g & y ^ x & y, p = (k >>> 28 | g << 4) ^ (k << 30 | g >>> 2) ^ (k << 25 | g >>> 7), u = (g >>> 28 | k << 4) ^ (g << 30 | k >>> 2) ^ (g << 25 | k >>> 7), c = ea[m], fa = c.high, da = c.low, c = E + ((h >>> 14 | l << 18) ^ (h >>> 18 | l << 14) ^ (h << 23 | l >>> 9)), q = V + ((l >>> 14 | h << 18) ^ (l >>> 18 | h << 14) ^ (l << 23 | h >>> 9)) + (c >>> 0 < E >>> 0 ? 1 :\n                            0), c = c + t, q = q + W + (c >>> 0 < t >>> 0 ? 1 : 0), c = c + da, q = q + fa + (c >>> 0 < da >>> 0 ? 1 : 0), c = c + b, q = q + j + (c >>> 0 < b >>> 0 ? 1 : 0), b = u + T, s = p + s + (b >>> 0 < u >>> 0 ? 1 : 0), V = S, E = D, S = R, D = C, R = l, C = h, h = B + c | 0, l = U + q + (h >>> 0 < B >>> 0 ? 1 : 0) | 0, U = A, B = y, A = z, y = x, z = k, x = g, g = c + b | 0, k = q + s + (g >>> 0 < c >>> 0 ? 1 : 0) | 0\n                } J = F.low = J + g; F.high = w + k + (J >>> 0 < g >>> 0 ? 1 : 0); K = e.low = K + x; e.high = X + z + (K >>> 0 < x >>> 0 ? 1 : 0); L = n.low = L + y; n.high = Y + A + (L >>> 0 < y >>> 0 ? 1 : 0); M = r.low = M + B; r.high = Z + U + (M >>> 0 < B >>> 0 ? 1 : 0); N = G.low = N + h; G.high = $ + l + (N >>> 0 < h >>> 0 ? 1 : 0); O = H.low = O + C; H.high = aa + R + (O >>> 0 < C >>> 0 ? 1 : 0); P = I.low = P + D;\n                I.high = ba + S + (P >>> 0 < D >>> 0 ? 1 : 0); Q = f.low = Q + E; f.high = ca + V + (Q >>> 0 < E >>> 0 ? 1 : 0)\n            }, _doFinalize: function () { var a = this._data, d = a.words, f = 8 * this._nDataBytes, e = 8 * a.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 128 >>> 10 << 5) + 30] = Math.floor(f / 4294967296); d[(e + 128 >>> 10 << 5) + 31] = f; a.sigBytes = 4 * d.length; this._process(); return this._hash.toX32() }, clone: function () { var a = r.clone.call(this); a._hash = this._hash.clone(); return a }, blockSize: 32\n        }); n.SHA512 = r._createHelper(e); n.HmacSHA512 = r._createHmacHelper(e)\n})();\n\n/*\nCryptoJS v3.1.2 sha384-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    var c = CryptoJS, a = c.x64, b = a.Word, e = a.WordArray, a = c.algo, d = a.SHA512, a = a.SHA384 = d.extend({ _doReset: function () { this._hash = new e.init([new b.init(3418070365, 3238371032), new b.init(1654270250, 914150663), new b.init(2438529370, 812702999), new b.init(355462360, 4144912697), new b.init(1731405415, 4290775857), new b.init(2394180231, 1750603025), new b.init(3675008525, 1694076839), new b.init(1203062813, 3204075428)]) }, _doFinalize: function () { var a = d._doFinalize.call(this); a.sigBytes -= 16; return a } }); c.SHA384 =\n        d._createHelper(a); c.HmacSHA384 = d._createHmacHelper(a)\n})();\n\n/*\nCryptoJS v3.1.2 ripemd160-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n/*\n\n(c) 2012 by Cedric Mesnil. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n    - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n    - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n(function () {\n    var q = CryptoJS, d = q.lib, n = d.WordArray, p = d.Hasher, d = q.algo, x = n.create([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]), y = n.create([5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]), z = n.create([11, 14, 15, 12,\n        5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]), A = n.create([8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]), B = n.create([0, 1518500249, 1859775393, 2400959708, 2840853838]), C = n.create([1352829926, 1548603684, 1836072691,\n            2053994217, 0]), d = d.RIPEMD160 = p.extend({\n                _doReset: function () { this._hash = n.create([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (e, v) {\n                    for (var b = 0; 16 > b; b++) { var c = v + b, f = e[c]; e[c] = (f << 8 | f >>> 24) & 16711935 | (f << 24 | f >>> 8) & 4278255360 } var c = this._hash.words, f = B.words, d = C.words, n = x.words, q = y.words, p = z.words, w = A.words, t, g, h, j, r, u, k, l, m, s; u = t = c[0]; k = g = c[1]; l = h = c[2]; m = j = c[3]; s = r = c[4]; for (var a, b = 0; 80 > b; b += 1)a = t + e[v + n[b]] | 0, a = 16 > b ? a + ((g ^ h ^ j) + f[0]) : 32 > b ? a + ((g & h | ~g & j) + f[1]) : 48 > b ?\n                        a + (((g | ~h) ^ j) + f[2]) : 64 > b ? a + ((g & j | h & ~j) + f[3]) : a + ((g ^ (h | ~j)) + f[4]), a |= 0, a = a << p[b] | a >>> 32 - p[b], a = a + r | 0, t = r, r = j, j = h << 10 | h >>> 22, h = g, g = a, a = u + e[v + q[b]] | 0, a = 16 > b ? a + ((k ^ (l | ~m)) + d[0]) : 32 > b ? a + ((k & m | l & ~m) + d[1]) : 48 > b ? a + (((k | ~l) ^ m) + d[2]) : 64 > b ? a + ((k & l | ~k & m) + d[3]) : a + ((k ^ l ^ m) + d[4]), a |= 0, a = a << w[b] | a >>> 32 - w[b], a = a + s | 0, u = s, s = m, m = l << 10 | l >>> 22, l = k, k = a; a = c[1] + h + m | 0; c[1] = c[2] + j + s | 0; c[2] = c[3] + r + u | 0; c[3] = c[4] + t + k | 0; c[4] = c[0] + g + l | 0; c[0] = a\n                }, _doFinalize: function () {\n                    var e = this._data, d = e.words, b = 8 * this._nDataBytes, c = 8 * e.sigBytes;\n                    d[c >>> 5] |= 128 << 24 - c % 32; d[(c + 64 >>> 9 << 4) + 14] = (b << 8 | b >>> 24) & 16711935 | (b << 24 | b >>> 8) & 4278255360; e.sigBytes = 4 * (d.length + 1); this._process(); e = this._hash; d = e.words; for (b = 0; 5 > b; b++)c = d[b], d[b] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360; return e\n                }, clone: function () { var d = p.clone.call(this); d._hash = this._hash.clone(); return d }\n            }); q.RIPEMD160 = p._createHelper(d); q.HmacRIPEMD160 = p._createHmacHelper(d)\n})(Math);\n\n/*\nCryptoJS v3.1.2 hmac.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    var c = CryptoJS, k = c.enc.Utf8; c.algo.HMAC = c.lib.Base.extend({\n        init: function (a, b) { a = this._hasher = new a.init; \"string\" == typeof b && (b = k.parse(b)); var c = a.blockSize, e = 4 * c; b.sigBytes > e && (b = a.finalize(b)); b.clamp(); for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++)h[d] ^= 1549556828, j[d] ^= 909522486; f.sigBytes = g.sigBytes = e; this.reset() }, reset: function () { var a = this._hasher; a.reset(); a.update(this._iKey) }, update: function (a) { this._hasher.update(a); return this }, finalize: function (a) {\n            var b =\n                this._hasher; a = b.finalize(a); b.reset(); return b.finalize(this._oKey.clone().concat(a))\n        }\n    })\n})();\n\n/*\nCryptoJS v3.1.2 pbkdf2-min.js\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    var b = CryptoJS, a = b.lib, d = a.Base, m = a.WordArray, a = b.algo, q = a.HMAC, l = a.PBKDF2 = d.extend({\n        cfg: d.extend({ keySize: 4, hasher: a.SHA1, iterations: 1 }), init: function (a) { this.cfg = this.cfg.extend(a) }, compute: function (a, b) {\n            for (var c = this.cfg, f = q.create(c.hasher, a), g = m.create(), d = m.create([1]), l = g.words, r = d.words, n = c.keySize, c = c.iterations; l.length < n;) {\n                var h = f.update(b).finalize(d); f.reset(); for (var j = h.words, s = j.length, k = h, p = 1; p < c; p++) { k = f.finalize(k); f.reset(); for (var t = k.words, e = 0; e < s; e++)j[e] ^= t[e] } g.concat(h);\n                r[0]++\n            } g.sigBytes = 4 * n; return g\n        }\n    }); b.PBKDF2 = function (a, b, c) { return l.create(c).compute(a, b) }\n})();\n\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nvar b64map = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"; var b64pad = \"=\"; function hex2b64(d) { var b; var e; var a = \"\"; for (b = 0; b + 3 <= d.length; b += 3) { e = parseInt(d.substring(b, b + 3), 16); a += b64map.charAt(e >> 6) + b64map.charAt(e & 63) } if (b + 1 == d.length) { e = parseInt(d.substring(b, b + 1), 16); a += b64map.charAt(e << 2) } else { if (b + 2 == d.length) { e = parseInt(d.substring(b, b + 2), 16); a += b64map.charAt(e >> 2) + b64map.charAt((e & 3) << 4) } } if (b64pad) { while ((a.length & 3) > 0) { a += b64pad } } return a } function b64tohex(f) { var d = \"\"; var e; var b = 0; var c; var a; for (e = 0; e < f.length; ++e) { if (f.charAt(e) == b64pad) { break } a = b64map.indexOf(f.charAt(e)); if (a < 0) { continue } if (b == 0) { d += int2char(a >> 2); c = a & 3; b = 1 } else { if (b == 1) { d += int2char((c << 2) | (a >> 4)); c = a & 15; b = 2 } else { if (b == 2) { d += int2char(c); d += int2char(a >> 2); c = a & 3; b = 3 } else { d += int2char((c << 2) | (a >> 4)); d += int2char(a & 15); b = 0 } } } } if (b == 1) { d += int2char(c << 2) } return d } function b64toBA(e) { var d = b64tohex(e); var c; var b = new Array(); for (c = 0; 2 * c < d.length; ++c) { b[c] = parseInt(d.substring(2 * c, 2 * c + 2), 16) } return b };\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nvar dbits; var canary = 244837814094590; var j_lm = ((canary & 16777215) == 15715070); function BigInteger(e, d, f) { if (e != null) { if (\"number\" == typeof e) { this.fromNumber(e, d, f) } else { if (d == null && \"string\" != typeof e) { this.fromString(e, 256) } else { this.fromString(e, d) } } } } function nbi() { return new BigInteger(null) } function am1(f, a, b, e, h, g) { while (--g >= 0) { var d = a * this[f++] + b[e] + h; h = Math.floor(d / 67108864); b[e++] = d & 67108863 } return h } function am2(f, q, r, e, o, a) { var k = q & 32767, p = q >> 15; while (--a >= 0) { var d = this[f] & 32767; var g = this[f++] >> 15; var b = p * d + g * k; d = k * d + ((b & 32767) << 15) + r[e] + (o & 1073741823); o = (d >>> 30) + (b >>> 15) + p * g + (o >>> 30); r[e++] = d & 1073741823 } return o } function am3(f, q, r, e, o, a) { var k = q & 16383, p = q >> 14; while (--a >= 0) { var d = this[f] & 16383; var g = this[f++] >> 14; var b = p * d + g * k; d = k * d + ((b & 16383) << 14) + r[e] + o; o = (d >> 28) + (b >> 14) + p * g; r[e++] = d & 268435455 } return o } if (j_lm && (navigator.appName == \"Microsoft Internet Explorer\")) { BigInteger.prototype.am = am2; dbits = 30 } else { if (j_lm && (navigator.appName != \"Netscape\")) { BigInteger.prototype.am = am1; dbits = 26 } else { BigInteger.prototype.am = am3; dbits = 28 } } BigInteger.prototype.DB = dbits; BigInteger.prototype.DM = ((1 << dbits) - 1); BigInteger.prototype.DV = (1 << dbits); var BI_FP = 52; BigInteger.prototype.FV = Math.pow(2, BI_FP); BigInteger.prototype.F1 = BI_FP - dbits; BigInteger.prototype.F2 = 2 * dbits - BI_FP; var BI_RM = \"0123456789abcdefghijklmnopqrstuvwxyz\"; var BI_RC = new Array(); var rr, vv; rr = \"0\".charCodeAt(0); for (vv = 0; vv <= 9; ++vv) { BI_RC[rr++] = vv } rr = \"a\".charCodeAt(0); for (vv = 10; vv < 36; ++vv) { BI_RC[rr++] = vv } rr = \"A\".charCodeAt(0); for (vv = 10; vv < 36; ++vv) { BI_RC[rr++] = vv } function int2char(a) { return BI_RM.charAt(a) } function intAt(b, a) { var d = BI_RC[b.charCodeAt(a)]; return (d == null) ? -1 : d } function bnpCopyTo(b) { for (var a = this.t - 1; a >= 0; --a) { b[a] = this[a] } b.t = this.t; b.s = this.s } function bnpFromInt(a) { this.t = 1; this.s = (a < 0) ? -1 : 0; if (a > 0) { this[0] = a } else { if (a < -1) { this[0] = a + this.DV } else { this.t = 0 } } } function nbv(a) { var b = nbi(); b.fromInt(a); return b } function bnpFromString(h, c) { var e; if (c == 16) { e = 4 } else { if (c == 8) { e = 3 } else { if (c == 256) { e = 8 } else { if (c == 2) { e = 1 } else { if (c == 32) { e = 5 } else { if (c == 4) { e = 2 } else { this.fromRadix(h, c); return } } } } } } this.t = 0; this.s = 0; var g = h.length, d = false, f = 0; while (--g >= 0) { var a = (e == 8) ? h[g] & 255 : intAt(h, g); if (a < 0) { if (h.charAt(g) == \"-\") { d = true } continue } d = false; if (f == 0) { this[this.t++] = a } else { if (f + e > this.DB) { this[this.t - 1] |= (a & ((1 << (this.DB - f)) - 1)) << f; this[this.t++] = (a >> (this.DB - f)) } else { this[this.t - 1] |= a << f } } f += e; if (f >= this.DB) { f -= this.DB } } if (e == 8 && (h[0] & 128) != 0) { this.s = -1; if (f > 0) { this[this.t - 1] |= ((1 << (this.DB - f)) - 1) << f } } this.clamp(); if (d) { BigInteger.ZERO.subTo(this, this) } } function bnpClamp() { var a = this.s & this.DM; while (this.t > 0 && this[this.t - 1] == a) { --this.t } } function bnToString(c) { if (this.s < 0) { return \"-\" + this.negate().toString(c) } var e; if (c == 16) { e = 4 } else { if (c == 8) { e = 3 } else { if (c == 2) { e = 1 } else { if (c == 32) { e = 5 } else { if (c == 4) { e = 2 } else { return this.toRadix(c) } } } } } var g = (1 << e) - 1, l, a = false, h = \"\", f = this.t; var j = this.DB - (f * this.DB) % e; if (f-- > 0) { if (j < this.DB && (l = this[f] >> j) > 0) { a = true; h = int2char(l) } while (f >= 0) { if (j < e) { l = (this[f] & ((1 << j) - 1)) << (e - j); l |= this[--f] >> (j += this.DB - e) } else { l = (this[f] >> (j -= e)) & g; if (j <= 0) { j += this.DB; --f } } if (l > 0) { a = true } if (a) { h += int2char(l) } } } return a ? h : \"0\" } function bnNegate() { var a = nbi(); BigInteger.ZERO.subTo(this, a); return a } function bnAbs() { return (this.s < 0) ? this.negate() : this } function bnCompareTo(b) { var d = this.s - b.s; if (d != 0) { return d } var c = this.t; d = c - b.t; if (d != 0) { return (this.s < 0) ? -d : d } while (--c >= 0) { if ((d = this[c] - b[c]) != 0) { return d } } return 0 } function nbits(a) { var c = 1, b; if ((b = a >>> 16) != 0) { a = b; c += 16 } if ((b = a >> 8) != 0) { a = b; c += 8 } if ((b = a >> 4) != 0) { a = b; c += 4 } if ((b = a >> 2) != 0) { a = b; c += 2 } if ((b = a >> 1) != 0) { a = b; c += 1 } return c } function bnBitLength() { if (this.t <= 0) { return 0 } return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)) } function bnpDLShiftTo(c, b) { var a; for (a = this.t - 1; a >= 0; --a) { b[a + c] = this[a] } for (a = c - 1; a >= 0; --a) { b[a] = 0 } b.t = this.t + c; b.s = this.s } function bnpDRShiftTo(c, b) { for (var a = c; a < this.t; ++a) { b[a - c] = this[a] } b.t = Math.max(this.t - c, 0); b.s = this.s } function bnpLShiftTo(j, e) { var b = j % this.DB; var a = this.DB - b; var g = (1 << a) - 1; var f = Math.floor(j / this.DB), h = (this.s << b) & this.DM, d; for (d = this.t - 1; d >= 0; --d) { e[d + f + 1] = (this[d] >> a) | h; h = (this[d] & g) << b } for (d = f - 1; d >= 0; --d) { e[d] = 0 } e[f] = h; e.t = this.t + f + 1; e.s = this.s; e.clamp() } function bnpRShiftTo(g, d) { d.s = this.s; var e = Math.floor(g / this.DB); if (e >= this.t) { d.t = 0; return } var b = g % this.DB; var a = this.DB - b; var f = (1 << b) - 1; d[0] = this[e] >> b; for (var c = e + 1; c < this.t; ++c) { d[c - e - 1] |= (this[c] & f) << a; d[c - e] = this[c] >> b } if (b > 0) { d[this.t - e - 1] |= (this.s & f) << a } d.t = this.t - e; d.clamp() } function bnpSubTo(d, f) { var e = 0, g = 0, b = Math.min(d.t, this.t); while (e < b) { g += this[e] - d[e]; f[e++] = g & this.DM; g >>= this.DB } if (d.t < this.t) { g -= d.s; while (e < this.t) { g += this[e]; f[e++] = g & this.DM; g >>= this.DB } g += this.s } else { g += this.s; while (e < d.t) { g -= d[e]; f[e++] = g & this.DM; g >>= this.DB } g -= d.s } f.s = (g < 0) ? -1 : 0; if (g < -1) { f[e++] = this.DV + g } else { if (g > 0) { f[e++] = g } } f.t = e; f.clamp() } function bnpMultiplyTo(c, e) { var b = this.abs(), f = c.abs(); var d = b.t; e.t = d + f.t; while (--d >= 0) { e[d] = 0 } for (d = 0; d < f.t; ++d) { e[d + b.t] = b.am(0, f[d], e, d, 0, b.t) } e.s = 0; e.clamp(); if (this.s != c.s) { BigInteger.ZERO.subTo(e, e) } } function bnpSquareTo(d) { var a = this.abs(); var b = d.t = 2 * a.t; while (--b >= 0) { d[b] = 0 } for (b = 0; b < a.t - 1; ++b) { var e = a.am(b, a[b], d, 2 * b, 0, 1); if ((d[b + a.t] += a.am(b + 1, 2 * a[b], d, 2 * b + 1, e, a.t - b - 1)) >= a.DV) { d[b + a.t] -= a.DV; d[b + a.t + 1] = 1 } } if (d.t > 0) { d[d.t - 1] += a.am(b, a[b], d, 2 * b, 0, 1) } d.s = 0; d.clamp() } function bnpDivRemTo(n, h, g) { var w = n.abs(); if (w.t <= 0) { return } var k = this.abs(); if (k.t < w.t) { if (h != null) { h.fromInt(0) } if (g != null) { this.copyTo(g) } return } if (g == null) { g = nbi() } var d = nbi(), a = this.s, l = n.s; var v = this.DB - nbits(w[w.t - 1]); if (v > 0) { w.lShiftTo(v, d); k.lShiftTo(v, g) } else { w.copyTo(d); k.copyTo(g) } var p = d.t; var b = d[p - 1]; if (b == 0) { return } var o = b * (1 << this.F1) + ((p > 1) ? d[p - 2] >> this.F2 : 0); var A = this.FV / o, z = (1 << this.F1) / o, x = 1 << this.F2; var u = g.t, s = u - p, f = (h == null) ? nbi() : h; d.dlShiftTo(s, f); if (g.compareTo(f) >= 0) { g[g.t++] = 1; g.subTo(f, g) } BigInteger.ONE.dlShiftTo(p, f); f.subTo(d, d); while (d.t < p) { d[d.t++] = 0 } while (--s >= 0) { var c = (g[--u] == b) ? this.DM : Math.floor(g[u] * A + (g[u - 1] + x) * z); if ((g[u] += d.am(0, c, g, s, 0, p)) < c) { d.dlShiftTo(s, f); g.subTo(f, g); while (g[u] < --c) { g.subTo(f, g) } } } if (h != null) { g.drShiftTo(p, h); if (a != l) { BigInteger.ZERO.subTo(h, h) } } g.t = p; g.clamp(); if (v > 0) { g.rShiftTo(v, g) } if (a < 0) { BigInteger.ZERO.subTo(g, g) } } function bnMod(b) { var c = nbi(); this.abs().divRemTo(b, null, c); if (this.s < 0 && c.compareTo(BigInteger.ZERO) > 0) { b.subTo(c, c) } return c } function Classic(a) { this.m = a } function cConvert(a) { if (a.s < 0 || a.compareTo(this.m) >= 0) { return a.mod(this.m) } else { return a } } function cRevert(a) { return a } function cReduce(a) { a.divRemTo(this.m, null, a) } function cMulTo(a, c, b) { a.multiplyTo(c, b); this.reduce(b) } function cSqrTo(a, b) { a.squareTo(b); this.reduce(b) } Classic.prototype.convert = cConvert; Classic.prototype.revert = cRevert; Classic.prototype.reduce = cReduce; Classic.prototype.mulTo = cMulTo; Classic.prototype.sqrTo = cSqrTo; function bnpInvDigit() { if (this.t < 1) { return 0 } var a = this[0]; if ((a & 1) == 0) { return 0 } var b = a & 3; b = (b * (2 - (a & 15) * b)) & 15; b = (b * (2 - (a & 255) * b)) & 255; b = (b * (2 - (((a & 65535) * b) & 65535))) & 65535; b = (b * (2 - a * b % this.DV)) % this.DV; return (b > 0) ? this.DV - b : -b } function Montgomery(a) { this.m = a; this.mp = a.invDigit(); this.mpl = this.mp & 32767; this.mph = this.mp >> 15; this.um = (1 << (a.DB - 15)) - 1; this.mt2 = 2 * a.t } function montConvert(a) { var b = nbi(); a.abs().dlShiftTo(this.m.t, b); b.divRemTo(this.m, null, b); if (a.s < 0 && b.compareTo(BigInteger.ZERO) > 0) { this.m.subTo(b, b) } return b } function montRevert(a) { var b = nbi(); a.copyTo(b); this.reduce(b); return b } function montReduce(a) { while (a.t <= this.mt2) { a[a.t++] = 0 } for (var c = 0; c < this.m.t; ++c) { var b = a[c] & 32767; var d = (b * this.mpl + (((b * this.mph + (a[c] >> 15) * this.mpl) & this.um) << 15)) & a.DM; b = c + this.m.t; a[b] += this.m.am(0, d, a, c, 0, this.m.t); while (a[b] >= a.DV) { a[b] -= a.DV; a[++b]++ } } a.clamp(); a.drShiftTo(this.m.t, a); if (a.compareTo(this.m) >= 0) { a.subTo(this.m, a) } } function montSqrTo(a, b) { a.squareTo(b); this.reduce(b) } function montMulTo(a, c, b) { a.multiplyTo(c, b); this.reduce(b) } Montgomery.prototype.convert = montConvert; Montgomery.prototype.revert = montRevert; Montgomery.prototype.reduce = montReduce; Montgomery.prototype.mulTo = montMulTo; Montgomery.prototype.sqrTo = montSqrTo; function bnpIsEven() { return ((this.t > 0) ? (this[0] & 1) : this.s) == 0 } function bnpExp(h, j) { if (h > 4294967295 || h < 1) { return BigInteger.ONE } var f = nbi(), a = nbi(), d = j.convert(this), c = nbits(h) - 1; d.copyTo(f); while (--c >= 0) { j.sqrTo(f, a); if ((h & (1 << c)) > 0) { j.mulTo(a, d, f) } else { var b = f; f = a; a = b } } return j.revert(f) } function bnModPowInt(b, a) { var c; if (b < 256 || a.isEven()) { c = new Classic(a) } else { c = new Montgomery(a) } return this.exp(b, c) } BigInteger.prototype.copyTo = bnpCopyTo; BigInteger.prototype.fromInt = bnpFromInt; BigInteger.prototype.fromString = bnpFromString; BigInteger.prototype.clamp = bnpClamp; BigInteger.prototype.dlShiftTo = bnpDLShiftTo; BigInteger.prototype.drShiftTo = bnpDRShiftTo; BigInteger.prototype.lShiftTo = bnpLShiftTo; BigInteger.prototype.rShiftTo = bnpRShiftTo; BigInteger.prototype.subTo = bnpSubTo; BigInteger.prototype.multiplyTo = bnpMultiplyTo; BigInteger.prototype.squareTo = bnpSquareTo; BigInteger.prototype.divRemTo = bnpDivRemTo; BigInteger.prototype.invDigit = bnpInvDigit; BigInteger.prototype.isEven = bnpIsEven; BigInteger.prototype.exp = bnpExp; BigInteger.prototype.toString = bnToString; BigInteger.prototype.negate = bnNegate; BigInteger.prototype.abs = bnAbs; BigInteger.prototype.compareTo = bnCompareTo; BigInteger.prototype.bitLength = bnBitLength; BigInteger.prototype.mod = bnMod; BigInteger.prototype.modPowInt = bnModPowInt; BigInteger.ZERO = nbv(0); BigInteger.ONE = nbv(1);\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nfunction bnClone() { var a = nbi(); this.copyTo(a); return a } function bnIntValue() { if (this.s < 0) { if (this.t == 1) { return this[0] - this.DV } else { if (this.t == 0) { return -1 } } } else { if (this.t == 1) { return this[0] } else { if (this.t == 0) { return 0 } } } return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0] } function bnByteValue() { return (this.t == 0) ? this.s : (this[0] << 24) >> 24 } function bnShortValue() { return (this.t == 0) ? this.s : (this[0] << 16) >> 16 } function bnpChunkSize(a) { return Math.floor(Math.LN2 * this.DB / Math.log(a)) } function bnSigNum() { if (this.s < 0) { return -1 } else { if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) { return 0 } else { return 1 } } } function bnpToRadix(c) { if (c == null) { c = 10 } if (this.signum() == 0 || c < 2 || c > 36) { return \"0\" } var f = this.chunkSize(c); var e = Math.pow(c, f); var i = nbv(e), j = nbi(), h = nbi(), g = \"\"; this.divRemTo(i, j, h); while (j.signum() > 0) { g = (e + h.intValue()).toString(c).substr(1) + g; j.divRemTo(i, j, h) } return h.intValue().toString(c) + g } function bnpFromRadix(m, h) { this.fromInt(0); if (h == null) { h = 10 } var f = this.chunkSize(h); var g = Math.pow(h, f), e = false, a = 0, l = 0; for (var c = 0; c < m.length; ++c) { var k = intAt(m, c); if (k < 0) { if (m.charAt(c) == \"-\" && this.signum() == 0) { e = true } continue } l = h * l + k; if (++a >= f) { this.dMultiply(g); this.dAddOffset(l, 0); a = 0; l = 0 } } if (a > 0) { this.dMultiply(Math.pow(h, a)); this.dAddOffset(l, 0) } if (e) { BigInteger.ZERO.subTo(this, this) } } function bnpFromNumber(f, e, h) { if (\"number\" == typeof e) { if (f < 2) { this.fromInt(1) } else { this.fromNumber(f, h); if (!this.testBit(f - 1)) { this.bitwiseTo(BigInteger.ONE.shiftLeft(f - 1), op_or, this) } if (this.isEven()) { this.dAddOffset(1, 0) } while (!this.isProbablePrime(e)) { this.dAddOffset(2, 0); if (this.bitLength() > f) { this.subTo(BigInteger.ONE.shiftLeft(f - 1), this) } } } } else { var d = new Array(), g = f & 7; d.length = (f >> 3) + 1; e.nextBytes(d); if (g > 0) { d[0] &= ((1 << g) - 1) } else { d[0] = 0 } this.fromString(d, 256) } } function bnToByteArray() { var b = this.t, c = new Array(); c[0] = this.s; var e = this.DB - (b * this.DB) % 8, f, a = 0; if (b-- > 0) { if (e < this.DB && (f = this[b] >> e) != (this.s & this.DM) >> e) { c[a++] = f | (this.s << (this.DB - e)) } while (b >= 0) { if (e < 8) { f = (this[b] & ((1 << e) - 1)) << (8 - e); f |= this[--b] >> (e += this.DB - 8) } else { f = (this[b] >> (e -= 8)) & 255; if (e <= 0) { e += this.DB; --b } } if ((f & 128) != 0) { f |= -256 } if (a == 0 && (this.s & 128) != (f & 128)) { ++a } if (a > 0 || f != this.s) { c[a++] = f } } } return c } function bnEquals(b) { return (this.compareTo(b) == 0) } function bnMin(b) { return (this.compareTo(b) < 0) ? this : b } function bnMax(b) { return (this.compareTo(b) > 0) ? this : b } function bnpBitwiseTo(c, h, e) { var d, g, b = Math.min(c.t, this.t); for (d = 0; d < b; ++d) { e[d] = h(this[d], c[d]) } if (c.t < this.t) { g = c.s & this.DM; for (d = b; d < this.t; ++d) { e[d] = h(this[d], g) } e.t = this.t } else { g = this.s & this.DM; for (d = b; d < c.t; ++d) { e[d] = h(g, c[d]) } e.t = c.t } e.s = h(this.s, c.s); e.clamp() } function op_and(a, b) { return a & b } function bnAnd(b) { var c = nbi(); this.bitwiseTo(b, op_and, c); return c } function op_or(a, b) { return a | b } function bnOr(b) { var c = nbi(); this.bitwiseTo(b, op_or, c); return c } function op_xor(a, b) { return a ^ b } function bnXor(b) { var c = nbi(); this.bitwiseTo(b, op_xor, c); return c } function op_andnot(a, b) { return a & ~b } function bnAndNot(b) { var c = nbi(); this.bitwiseTo(b, op_andnot, c); return c } function bnNot() { var b = nbi(); for (var a = 0; a < this.t; ++a) { b[a] = this.DM & ~this[a] } b.t = this.t; b.s = ~this.s; return b } function bnShiftLeft(b) { var a = nbi(); if (b < 0) { this.rShiftTo(-b, a) } else { this.lShiftTo(b, a) } return a } function bnShiftRight(b) { var a = nbi(); if (b < 0) { this.lShiftTo(-b, a) } else { this.rShiftTo(b, a) } return a } function lbit(a) { if (a == 0) { return -1 } var b = 0; if ((a & 65535) == 0) { a >>= 16; b += 16 } if ((a & 255) == 0) { a >>= 8; b += 8 } if ((a & 15) == 0) { a >>= 4; b += 4 } if ((a & 3) == 0) { a >>= 2; b += 2 } if ((a & 1) == 0) { ++b } return b } function bnGetLowestSetBit() { for (var a = 0; a < this.t; ++a) { if (this[a] != 0) { return a * this.DB + lbit(this[a]) } } if (this.s < 0) { return this.t * this.DB } return -1 } function cbit(a) { var b = 0; while (a != 0) { a &= a - 1; ++b } return b } function bnBitCount() { var c = 0, a = this.s & this.DM; for (var b = 0; b < this.t; ++b) { c += cbit(this[b] ^ a) } return c } function bnTestBit(b) { var a = Math.floor(b / this.DB); if (a >= this.t) { return (this.s != 0) } return ((this[a] & (1 << (b % this.DB))) != 0) } function bnpChangeBit(c, b) { var a = BigInteger.ONE.shiftLeft(c); this.bitwiseTo(a, b, a); return a } function bnSetBit(a) { return this.changeBit(a, op_or) } function bnClearBit(a) { return this.changeBit(a, op_andnot) } function bnFlipBit(a) { return this.changeBit(a, op_xor) } function bnpAddTo(d, f) { var e = 0, g = 0, b = Math.min(d.t, this.t); while (e < b) { g += this[e] + d[e]; f[e++] = g & this.DM; g >>= this.DB } if (d.t < this.t) { g += d.s; while (e < this.t) { g += this[e]; f[e++] = g & this.DM; g >>= this.DB } g += this.s } else { g += this.s; while (e < d.t) { g += d[e]; f[e++] = g & this.DM; g >>= this.DB } g += d.s } f.s = (g < 0) ? -1 : 0; if (g > 0) { f[e++] = g } else { if (g < -1) { f[e++] = this.DV + g } } f.t = e; f.clamp() } function bnAdd(b) { var c = nbi(); this.addTo(b, c); return c } function bnSubtract(b) { var c = nbi(); this.subTo(b, c); return c } function bnMultiply(b) { var c = nbi(); this.multiplyTo(b, c); return c } function bnSquare() { var a = nbi(); this.squareTo(a); return a } function bnDivide(b) { var c = nbi(); this.divRemTo(b, c, null); return c } function bnRemainder(b) { var c = nbi(); this.divRemTo(b, null, c); return c } function bnDivideAndRemainder(b) { var d = nbi(), c = nbi(); this.divRemTo(b, d, c); return new Array(d, c) } function bnpDMultiply(a) { this[this.t] = this.am(0, a - 1, this, 0, 0, this.t); ++this.t; this.clamp() } function bnpDAddOffset(b, a) { if (b == 0) { return } while (this.t <= a) { this[this.t++] = 0 } this[a] += b; while (this[a] >= this.DV) { this[a] -= this.DV; if (++a >= this.t) { this[this.t++] = 0 } ++this[a] } } function NullExp() { } function nNop(a) { return a } function nMulTo(a, c, b) { a.multiplyTo(c, b) } function nSqrTo(a, b) { a.squareTo(b) } NullExp.prototype.convert = nNop; NullExp.prototype.revert = nNop; NullExp.prototype.mulTo = nMulTo; NullExp.prototype.sqrTo = nSqrTo; function bnPow(a) { return this.exp(a, new NullExp()) } function bnpMultiplyLowerTo(b, f, e) { var d = Math.min(this.t + b.t, f); e.s = 0; e.t = d; while (d > 0) { e[--d] = 0 } var c; for (c = e.t - this.t; d < c; ++d) { e[d + this.t] = this.am(0, b[d], e, d, 0, this.t) } for (c = Math.min(b.t, f); d < c; ++d) { this.am(0, b[d], e, d, 0, f - d) } e.clamp() } function bnpMultiplyUpperTo(b, e, d) { --e; var c = d.t = this.t + b.t - e; d.s = 0; while (--c >= 0) { d[c] = 0 } for (c = Math.max(e - this.t, 0); c < b.t; ++c) { d[this.t + c - e] = this.am(e - c, b[c], d, 0, 0, this.t + c - e) } d.clamp(); d.drShiftTo(1, d) } function Barrett(a) { this.r2 = nbi(); this.q3 = nbi(); BigInteger.ONE.dlShiftTo(2 * a.t, this.r2); this.mu = this.r2.divide(a); this.m = a } function barrettConvert(a) { if (a.s < 0 || a.t > 2 * this.m.t) { return a.mod(this.m) } else { if (a.compareTo(this.m) < 0) { return a } else { var b = nbi(); a.copyTo(b); this.reduce(b); return b } } } function barrettRevert(a) { return a } function barrettReduce(a) { a.drShiftTo(this.m.t - 1, this.r2); if (a.t > this.m.t + 1) { a.t = this.m.t + 1; a.clamp() } this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); while (a.compareTo(this.r2) < 0) { a.dAddOffset(1, this.m.t + 1) } a.subTo(this.r2, a); while (a.compareTo(this.m) >= 0) { a.subTo(this.m, a) } } function barrettSqrTo(a, b) { a.squareTo(b); this.reduce(b) } function barrettMulTo(a, c, b) { a.multiplyTo(c, b); this.reduce(b) } Barrett.prototype.convert = barrettConvert; Barrett.prototype.revert = barrettRevert; Barrett.prototype.reduce = barrettReduce; Barrett.prototype.mulTo = barrettMulTo; Barrett.prototype.sqrTo = barrettSqrTo; function bnModPow(q, f) { var o = q.bitLength(), h, b = nbv(1), v; if (o <= 0) { return b } else { if (o < 18) { h = 1 } else { if (o < 48) { h = 3 } else { if (o < 144) { h = 4 } else { if (o < 768) { h = 5 } else { h = 6 } } } } } if (o < 8) { v = new Classic(f) } else { if (f.isEven()) { v = new Barrett(f) } else { v = new Montgomery(f) } } var p = new Array(), d = 3, s = h - 1, a = (1 << h) - 1; p[1] = v.convert(this); if (h > 1) { var A = nbi(); v.sqrTo(p[1], A); while (d <= a) { p[d] = nbi(); v.mulTo(A, p[d - 2], p[d]); d += 2 } } var l = q.t - 1, x, u = true, c = nbi(), y; o = nbits(q[l]) - 1; while (l >= 0) { if (o >= s) { x = (q[l] >> (o - s)) & a } else { x = (q[l] & ((1 << (o + 1)) - 1)) << (s - o); if (l > 0) { x |= q[l - 1] >> (this.DB + o - s) } } d = h; while ((x & 1) == 0) { x >>= 1; --d } if ((o -= d) < 0) { o += this.DB; --l } if (u) { p[x].copyTo(b); u = false } else { while (d > 1) { v.sqrTo(b, c); v.sqrTo(c, b); d -= 2 } if (d > 0) { v.sqrTo(b, c) } else { y = b; b = c; c = y } v.mulTo(c, p[x], b) } while (l >= 0 && (q[l] & (1 << o)) == 0) { v.sqrTo(b, c); y = b; b = c; c = y; if (--o < 0) { o = this.DB - 1; --l } } } return v.revert(b) } function bnGCD(c) { var b = (this.s < 0) ? this.negate() : this.clone(); var h = (c.s < 0) ? c.negate() : c.clone(); if (b.compareTo(h) < 0) { var e = b; b = h; h = e } var d = b.getLowestSetBit(), f = h.getLowestSetBit(); if (f < 0) { return b } if (d < f) { f = d } if (f > 0) { b.rShiftTo(f, b); h.rShiftTo(f, h) } while (b.signum() > 0) { if ((d = b.getLowestSetBit()) > 0) { b.rShiftTo(d, b) } if ((d = h.getLowestSetBit()) > 0) { h.rShiftTo(d, h) } if (b.compareTo(h) >= 0) { b.subTo(h, b); b.rShiftTo(1, b) } else { h.subTo(b, h); h.rShiftTo(1, h) } } if (f > 0) { h.lShiftTo(f, h) } return h } function bnpModInt(e) { if (e <= 0) { return 0 } var c = this.DV % e, b = (this.s < 0) ? e - 1 : 0; if (this.t > 0) { if (c == 0) { b = this[0] % e } else { for (var a = this.t - 1; a >= 0; --a) { b = (c * b + this[a]) % e } } } return b } function bnModInverse(f) { var j = f.isEven(); if ((this.isEven() && j) || f.signum() == 0) { return BigInteger.ZERO } var i = f.clone(), h = this.clone(); var g = nbv(1), e = nbv(0), l = nbv(0), k = nbv(1); while (i.signum() != 0) { while (i.isEven()) { i.rShiftTo(1, i); if (j) { if (!g.isEven() || !e.isEven()) { g.addTo(this, g); e.subTo(f, e) } g.rShiftTo(1, g) } else { if (!e.isEven()) { e.subTo(f, e) } } e.rShiftTo(1, e) } while (h.isEven()) { h.rShiftTo(1, h); if (j) { if (!l.isEven() || !k.isEven()) { l.addTo(this, l); k.subTo(f, k) } l.rShiftTo(1, l) } else { if (!k.isEven()) { k.subTo(f, k) } } k.rShiftTo(1, k) } if (i.compareTo(h) >= 0) { i.subTo(h, i); if (j) { g.subTo(l, g) } e.subTo(k, e) } else { h.subTo(i, h); if (j) { l.subTo(g, l) } k.subTo(e, k) } } if (h.compareTo(BigInteger.ONE) != 0) { return BigInteger.ZERO } if (k.compareTo(f) >= 0) { return k.subtract(f) } if (k.signum() < 0) { k.addTo(f, k) } else { return k } if (k.signum() < 0) { return k.add(f) } else { return k } } var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]; var lplim = (1 << 26) / lowprimes[lowprimes.length - 1]; function bnIsProbablePrime(e) { var d, b = this.abs(); if (b.t == 1 && b[0] <= lowprimes[lowprimes.length - 1]) { for (d = 0; d < lowprimes.length; ++d) { if (b[0] == lowprimes[d]) { return true } } return false } if (b.isEven()) { return false } d = 1; while (d < lowprimes.length) { var a = lowprimes[d], c = d + 1; while (c < lowprimes.length && a < lplim) { a *= lowprimes[c++] } a = b.modInt(a); while (d < c) { if (a % lowprimes[d++] == 0) { return false } } } return b.millerRabin(e) } function bnpMillerRabin(f) { var g = this.subtract(BigInteger.ONE); var c = g.getLowestSetBit(); if (c <= 0) { return false } var h = g.shiftRight(c); f = (f + 1) >> 1; if (f > lowprimes.length) { f = lowprimes.length } var b = nbi(); for (var e = 0; e < f; ++e) { b.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]); var l = b.modPow(h, this); if (l.compareTo(BigInteger.ONE) != 0 && l.compareTo(g) != 0) { var d = 1; while (d++ < c && l.compareTo(g) != 0) { l = l.modPowInt(2, this); if (l.compareTo(BigInteger.ONE) == 0) { return false } } if (l.compareTo(g) != 0) { return false } } } return true } BigInteger.prototype.chunkSize = bnpChunkSize; BigInteger.prototype.toRadix = bnpToRadix; BigInteger.prototype.fromRadix = bnpFromRadix; BigInteger.prototype.fromNumber = bnpFromNumber; BigInteger.prototype.bitwiseTo = bnpBitwiseTo; BigInteger.prototype.changeBit = bnpChangeBit; BigInteger.prototype.addTo = bnpAddTo; BigInteger.prototype.dMultiply = bnpDMultiply; BigInteger.prototype.dAddOffset = bnpDAddOffset; BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; BigInteger.prototype.modInt = bnpModInt; BigInteger.prototype.millerRabin = bnpMillerRabin; BigInteger.prototype.clone = bnClone; BigInteger.prototype.intValue = bnIntValue; BigInteger.prototype.byteValue = bnByteValue; BigInteger.prototype.shortValue = bnShortValue; BigInteger.prototype.signum = bnSigNum; BigInteger.prototype.toByteArray = bnToByteArray; BigInteger.prototype.equals = bnEquals; BigInteger.prototype.min = bnMin; BigInteger.prototype.max = bnMax; BigInteger.prototype.and = bnAnd; BigInteger.prototype.or = bnOr; BigInteger.prototype.xor = bnXor; BigInteger.prototype.andNot = bnAndNot; BigInteger.prototype.not = bnNot; BigInteger.prototype.shiftLeft = bnShiftLeft; BigInteger.prototype.shiftRight = bnShiftRight; BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; BigInteger.prototype.bitCount = bnBitCount; BigInteger.prototype.testBit = bnTestBit; BigInteger.prototype.setBit = bnSetBit; BigInteger.prototype.clearBit = bnClearBit; BigInteger.prototype.flipBit = bnFlipBit; BigInteger.prototype.add = bnAdd; BigInteger.prototype.subtract = bnSubtract; BigInteger.prototype.multiply = bnMultiply; BigInteger.prototype.divide = bnDivide; BigInteger.prototype.remainder = bnRemainder; BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; BigInteger.prototype.modPow = bnModPow; BigInteger.prototype.modInverse = bnModInverse; BigInteger.prototype.pow = bnPow; BigInteger.prototype.gcd = bnGCD; BigInteger.prototype.isProbablePrime = bnIsProbablePrime; BigInteger.prototype.square = bnSquare;\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nfunction Arcfour() { this.i = 0; this.j = 0; this.S = new Array() } function ARC4init(d) { var c, a, b; for (c = 0; c < 256; ++c) { this.S[c] = c } a = 0; for (c = 0; c < 256; ++c) { a = (a + this.S[c] + d[c % d.length]) & 255; b = this.S[c]; this.S[c] = this.S[a]; this.S[a] = b } this.i = 0; this.j = 0 } function ARC4next() { var a; this.i = (this.i + 1) & 255; this.j = (this.j + this.S[this.i]) & 255; a = this.S[this.i]; this.S[this.i] = this.S[this.j]; this.S[this.j] = a; return this.S[(a + this.S[this.i]) & 255] } Arcfour.prototype.init = ARC4init; Arcfour.prototype.next = ARC4next; function prng_newstate() { return new Arcfour() } var rng_psize = 256;\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nvar rng_state; var rng_pool; var rng_pptr; function rng_seed_int(a) { rng_pool[rng_pptr++] ^= a & 255; rng_pool[rng_pptr++] ^= (a >> 8) & 255; rng_pool[rng_pptr++] ^= (a >> 16) & 255; rng_pool[rng_pptr++] ^= (a >> 24) & 255; if (rng_pptr >= rng_psize) { rng_pptr -= rng_psize } } function rng_seed_time() { rng_seed_int(new Date().getTime()) } if (rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; if (window !== undefined && (window.crypto !== undefined || window.msCrypto !== undefined)) { var crypto = window.crypto || window.msCrypto; if (crypto.getRandomValues) { var ua = new Uint8Array(32); crypto.getRandomValues(ua); for (t = 0; t < 32; ++t) { rng_pool[rng_pptr++] = ua[t] } } else { if (navigator.appName == \"Netscape\" && navigator.appVersion < \"5\") { var z = window.crypto.random(32); for (t = 0; t < z.length; ++t) { rng_pool[rng_pptr++] = z.charCodeAt(t) & 255 } } } } while (rng_pptr < rng_psize) { t = Math.floor(65536 * Math.random()); rng_pool[rng_pptr++] = t >>> 8; rng_pool[rng_pptr++] = t & 255 } rng_pptr = 0; rng_seed_time() } function rng_get_byte() { if (rng_state == null) { rng_seed_time(); rng_state = prng_newstate(); rng_state.init(rng_pool); for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) { rng_pool[rng_pptr] = 0 } rng_pptr = 0 } return rng_state.next() } function rng_get_bytes(b) { var a; for (a = 0; a < b.length; ++a) { b[a] = rng_get_byte() } } function SecureRandom() { } SecureRandom.prototype.nextBytes = rng_get_bytes;\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nfunction parseBigInt(b, a) { return new BigInteger(b, a) } function linebrk(c, d) { var a = \"\"; var b = 0; while (b + d < c.length) { a += c.substring(b, b + d) + \"\\n\"; b += d } return a + c.substring(b, c.length) } function byte2Hex(a) { if (a < 16) { return \"0\" + a.toString(16) } else { return a.toString(16) } } function pkcs1pad2(e, h) { if (h < e.length + 11) { alert(\"Message too long for RSA\"); return null } var g = new Array(); var d = e.length - 1; while (d >= 0 && h > 0) { var f = e.charCodeAt(d--); if (f < 128) { g[--h] = f } else { if ((f > 127) && (f < 2048)) { g[--h] = (f & 63) | 128; g[--h] = (f >> 6) | 192 } else { g[--h] = (f & 63) | 128; g[--h] = ((f >> 6) & 63) | 128; g[--h] = (f >> 12) | 224 } } } g[--h] = 0; var b = new SecureRandom(); var a = new Array(); while (h > 2) { a[0] = 0; while (a[0] == 0) { b.nextBytes(a) } g[--h] = a[0] } g[--h] = 2; g[--h] = 0; return new BigInteger(g) } function oaep_mgf1_arr(c, a, e) { var b = \"\", d = 0; while (b.length < a) { b += e(String.fromCharCode.apply(String, c.concat([(d & 4278190080) >> 24, (d & 16711680) >> 16, (d & 65280) >> 8, d & 255]))); d += 1 } return b } function oaep_pad(q, a, f, l) { var c = KJUR.crypto.MessageDigest; var o = KJUR.crypto.Util; var b = null; if (!f) { f = \"sha1\" } if (typeof f === \"string\") { b = c.getCanonicalAlgName(f); l = c.getHashLength(b); f = function (i) { return hextorstr(o.hashString(i, b)) } } if (q.length + 2 * l + 2 > a) { throw \"Message too long for RSA\" } var k = \"\", e; for (e = 0; e < a - q.length - 2 * l - 2; e += 1) { k += \"\\x00\" } var h = f(\"\") + k + \"\\x01\" + q; var g = new Array(l); new SecureRandom().nextBytes(g); var j = oaep_mgf1_arr(g, h.length, f); var p = []; for (e = 0; e < h.length; e += 1) { p[e] = h.charCodeAt(e) ^ j.charCodeAt(e) } var m = oaep_mgf1_arr(p, g.length, f); var d = [0]; for (e = 0; e < g.length; e += 1) { d[e + 1] = g[e] ^ m.charCodeAt(e) } return new BigInteger(d.concat(p)) } function RSAKey() { this.n = null; this.e = 0; this.d = null; this.p = null; this.q = null; this.dmp1 = null; this.dmq1 = null; this.coeff = null } function RSASetPublic(b, a) { this.isPublic = true; this.isPrivate = false; if (typeof b !== \"string\") { this.n = b; this.e = a } else { if (b != null && a != null && b.length > 0 && a.length > 0) { this.n = parseBigInt(b, 16); this.e = parseInt(a, 16) } else { throw \"Invalid RSA public key\" } } } function RSADoPublic(a) { return a.modPowInt(this.e, this.n) } function RSAEncrypt(d) { var a = pkcs1pad2(d, (this.n.bitLength() + 7) >> 3); if (a == null) { return null } var e = this.doPublic(a); if (e == null) { return null } var b = e.toString(16); if ((b.length & 1) == 0) { return b } else { return \"0\" + b } } function RSAEncryptOAEP(f, e, b) { var a = oaep_pad(f, (this.n.bitLength() + 7) >> 3, e, b); if (a == null) { return null } var g = this.doPublic(a); if (g == null) { return null } var d = g.toString(16); if ((d.length & 1) == 0) { return d } else { return \"0\" + d } } RSAKey.prototype.doPublic = RSADoPublic; RSAKey.prototype.setPublic = RSASetPublic; RSAKey.prototype.encrypt = RSAEncrypt; RSAKey.prototype.encryptOAEP = RSAEncryptOAEP; RSAKey.prototype.type = \"RSA\";\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nfunction pkcs1unpad2(g, j) { var a = g.toByteArray(); var f = 0; while (f < a.length && a[f] == 0) { ++f } if (a.length - f != j - 1 || a[f] != 2) { return null } ++f; while (a[f] != 0) { if (++f >= a.length) { return null } } var e = \"\"; while (++f < a.length) { var h = a[f] & 255; if (h < 128) { e += String.fromCharCode(h) } else { if ((h > 191) && (h < 224)) { e += String.fromCharCode(((h & 31) << 6) | (a[f + 1] & 63)); ++f } else { e += String.fromCharCode(((h & 15) << 12) | ((a[f + 1] & 63) << 6) | (a[f + 2] & 63)); f += 2 } } } return e } function oaep_mgf1_str(c, a, e) { var b = \"\", d = 0; while (b.length < a) { b += e(c + String.fromCharCode.apply(String, [(d & 4278190080) >> 24, (d & 16711680) >> 16, (d & 65280) >> 8, d & 255])); d += 1 } return b } function oaep_unpad(o, b, g, p) { var e = KJUR.crypto.MessageDigest; var r = KJUR.crypto.Util; var c = null; if (!g) { g = \"sha1\" } if (typeof g === \"string\") { c = e.getCanonicalAlgName(g); p = e.getHashLength(c); g = function (d) { return hextorstr(r.hashString(d, c)) } } o = o.toByteArray(); var h; for (h = 0; h < o.length; h += 1) { o[h] &= 255 } while (o.length < b) { o.unshift(0) } o = String.fromCharCode.apply(String, o); if (o.length < 2 * p + 2) { throw \"Cipher too short\" } var f = o.substr(1, p); var s = o.substr(p + 1); var q = oaep_mgf1_str(s, p, g); var k = [], h; for (h = 0; h < f.length; h += 1) { k[h] = f.charCodeAt(h) ^ q.charCodeAt(h) } var l = oaep_mgf1_str(String.fromCharCode.apply(String, k), o.length - p, g); var j = []; for (h = 0; h < s.length; h += 1) { j[h] = s.charCodeAt(h) ^ l.charCodeAt(h) } j = String.fromCharCode.apply(String, j); if (j.substr(0, p) !== g(\"\")) { throw \"Hash mismatch\" } j = j.substr(p); var a = j.indexOf(\"\\x01\"); var m = (a != -1) ? j.substr(0, a).lastIndexOf(\"\\x00\") : -1; if (m + 1 != a) { throw \"Malformed data\" } return j.substr(a + 1) } function RSASetPrivate(c, a, b) { this.isPrivate = true; if (typeof c !== \"string\") { this.n = c; this.e = a; this.d = b } else { if (c != null && a != null && c.length > 0 && a.length > 0) { this.n = parseBigInt(c, 16); this.e = parseInt(a, 16); this.d = parseBigInt(b, 16) } else { alert(\"Invalid RSA private key\") } } } function RSASetPrivateEx(g, d, e, c, b, a, h, f) { this.isPrivate = true; this.isPublic = false; if (g == null) { throw \"RSASetPrivateEx N == null\" } if (d == null) { throw \"RSASetPrivateEx E == null\" } if (g.length == 0) { throw \"RSASetPrivateEx N.length == 0\" } if (d.length == 0) { throw \"RSASetPrivateEx E.length == 0\" } if (g != null && d != null && g.length > 0 && d.length > 0) { this.n = parseBigInt(g, 16); this.e = parseInt(d, 16); this.d = parseBigInt(e, 16); this.p = parseBigInt(c, 16); this.q = parseBigInt(b, 16); this.dmp1 = parseBigInt(a, 16); this.dmq1 = parseBigInt(h, 16); this.coeff = parseBigInt(f, 16) } else { alert(\"Invalid RSA private key in RSASetPrivateEx\") } } function RSAGenerate(b, i) { var a = new SecureRandom(); var f = b >> 1; this.e = parseInt(i, 16); var c = new BigInteger(i, 16); for (; ;) { for (; ;) { this.p = new BigInteger(b - f, 1, a); if (this.p.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) { break } } for (; ;) { this.q = new BigInteger(f, 1, a); if (this.q.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) { break } } if (this.p.compareTo(this.q) <= 0) { var h = this.p; this.p = this.q; this.q = h } var g = this.p.subtract(BigInteger.ONE); var d = this.q.subtract(BigInteger.ONE); var e = g.multiply(d); if (e.gcd(c).compareTo(BigInteger.ONE) == 0) { this.n = this.p.multiply(this.q); this.d = c.modInverse(e); this.dmp1 = this.d.mod(g); this.dmq1 = this.d.mod(d); this.coeff = this.q.modInverse(this.p); break } } this.isPrivate = true } function RSADoPrivate(a) { if (this.p == null || this.q == null) { return a.modPow(this.d, this.n) } var c = a.mod(this.p).modPow(this.dmp1, this.p); var b = a.mod(this.q).modPow(this.dmq1, this.q); while (c.compareTo(b) < 0) { c = c.add(this.p) } return c.subtract(b).multiply(this.coeff).mod(this.p).multiply(this.q).add(b) } function RSADecrypt(b) { var d = parseBigInt(b, 16); var a = this.doPrivate(d); if (a == null) { return null } return pkcs1unpad2(a, (this.n.bitLength() + 7) >> 3) } function RSADecryptOAEP(e, d, b) { var f = parseBigInt(e, 16); var a = this.doPrivate(f); if (a == null) { return null } return oaep_unpad(a, (this.n.bitLength() + 7) >> 3, d, b) } RSAKey.prototype.doPrivate = RSADoPrivate; RSAKey.prototype.setPrivate = RSASetPrivate; RSAKey.prototype.setPrivateEx = RSASetPrivateEx; RSAKey.prototype.generate = RSAGenerate; RSAKey.prototype.decrypt = RSADecrypt; RSAKey.prototype.decryptOAEP = RSADecryptOAEP;\n/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/\n */\nfunction ECFieldElementFp(b, a) { this.x = a; this.q = b } function feFpEquals(a) { if (a == this) { return true } return (this.q.equals(a.q) && this.x.equals(a.x)) } function feFpToBigInteger() { return this.x } function feFpNegate() { return new ECFieldElementFp(this.q, this.x.negate().mod(this.q)) } function feFpAdd(a) { return new ECFieldElementFp(this.q, this.x.add(a.toBigInteger()).mod(this.q)) } function feFpSubtract(a) { return new ECFieldElementFp(this.q, this.x.subtract(a.toBigInteger()).mod(this.q)) } function feFpMultiply(a) { return new ECFieldElementFp(this.q, this.x.multiply(a.toBigInteger()).mod(this.q)) } function feFpSquare() { return new ECFieldElementFp(this.q, this.x.square().mod(this.q)) } function feFpDivide(a) { return new ECFieldElementFp(this.q, this.x.multiply(a.toBigInteger().modInverse(this.q)).mod(this.q)) } ECFieldElementFp.prototype.equals = feFpEquals; ECFieldElementFp.prototype.toBigInteger = feFpToBigInteger; ECFieldElementFp.prototype.negate = feFpNegate; ECFieldElementFp.prototype.add = feFpAdd; ECFieldElementFp.prototype.subtract = feFpSubtract; ECFieldElementFp.prototype.multiply = feFpMultiply; ECFieldElementFp.prototype.square = feFpSquare; ECFieldElementFp.prototype.divide = feFpDivide; function ECPointFp(c, a, d, b) { this.curve = c; this.x = a; this.y = d; if (b == null) { this.z = BigInteger.ONE } else { this.z = b } this.zinv = null } function pointFpGetX() { if (this.zinv == null) { this.zinv = this.z.modInverse(this.curve.q) } return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q)) } function pointFpGetY() { if (this.zinv == null) { this.zinv = this.z.modInverse(this.curve.q) } return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q)) } function pointFpEquals(a) { if (a == this) { return true } if (this.isInfinity()) { return a.isInfinity() } if (a.isInfinity()) { return this.isInfinity() } var c, b; c = a.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(a.z)).mod(this.curve.q); if (!c.equals(BigInteger.ZERO)) { return false } b = a.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(a.z)).mod(this.curve.q); return b.equals(BigInteger.ZERO) } function pointFpIsInfinity() { if ((this.x == null) && (this.y == null)) { return true } return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO) } function pointFpNegate() { return new ECPointFp(this.curve, this.x, this.y.negate(), this.z) } function pointFpAdd(l) { if (this.isInfinity()) { return l } if (l.isInfinity()) { return this } var p = l.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(l.z)).mod(this.curve.q); var o = l.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(l.z)).mod(this.curve.q); if (BigInteger.ZERO.equals(o)) { if (BigInteger.ZERO.equals(p)) { return this.twice() } return this.curve.getInfinity() } var j = new BigInteger(\"3\"); var e = this.x.toBigInteger(); var n = this.y.toBigInteger(); var c = l.x.toBigInteger(); var k = l.y.toBigInteger(); var m = o.square(); var i = m.multiply(o); var d = e.multiply(m); var g = p.square().multiply(this.z); var a = g.subtract(d.shiftLeft(1)).multiply(l.z).subtract(i).multiply(o).mod(this.curve.q); var h = d.multiply(j).multiply(p).subtract(n.multiply(i)).subtract(g.multiply(p)).multiply(l.z).add(p.multiply(i)).mod(this.curve.q); var f = i.multiply(this.z).multiply(l.z).mod(this.curve.q); return new ECPointFp(this.curve, this.curve.fromBigInteger(a), this.curve.fromBigInteger(h), f) } function pointFpTwice() { if (this.isInfinity()) { return this } if (this.y.toBigInteger().signum() == 0) { return this.curve.getInfinity() } var g = new BigInteger(\"3\"); var c = this.x.toBigInteger(); var h = this.y.toBigInteger(); var e = h.multiply(this.z); var j = e.multiply(h).mod(this.curve.q); var i = this.curve.a.toBigInteger(); var k = c.square().multiply(g); if (!BigInteger.ZERO.equals(i)) { k = k.add(this.z.square().multiply(i)) } k = k.mod(this.curve.q); var b = k.square().subtract(c.shiftLeft(3).multiply(j)).shiftLeft(1).multiply(e).mod(this.curve.q); var f = k.multiply(g).multiply(c).subtract(j.shiftLeft(1)).shiftLeft(2).multiply(j).subtract(k.square().multiply(k)).mod(this.curve.q); var d = e.square().multiply(e).shiftLeft(3).mod(this.curve.q); return new ECPointFp(this.curve, this.curve.fromBigInteger(b), this.curve.fromBigInteger(f), d) } function pointFpMultiply(b) { if (this.isInfinity()) { return this } if (b.signum() == 0) { return this.curve.getInfinity() } var g = b; var f = g.multiply(new BigInteger(\"3\")); var l = this.negate(); var d = this; var c; for (c = f.bitLength() - 2; c > 0; --c) { d = d.twice(); var a = f.testBit(c); var j = g.testBit(c); if (a != j) { d = d.add(a ? this : l) } } return d } function pointFpMultiplyTwo(c, a, b) { var d; if (c.bitLength() > b.bitLength()) { d = c.bitLength() - 1 } else { d = b.bitLength() - 1 } var f = this.curve.getInfinity(); var e = this.add(a); while (d >= 0) { f = f.twice(); if (c.testBit(d)) { if (b.testBit(d)) { f = f.add(e) } else { f = f.add(this) } } else { if (b.testBit(d)) { f = f.add(a) } } --d } return f } ECPointFp.prototype.getX = pointFpGetX; ECPointFp.prototype.getY = pointFpGetY; ECPointFp.prototype.equals = pointFpEquals; ECPointFp.prototype.isInfinity = pointFpIsInfinity; ECPointFp.prototype.negate = pointFpNegate; ECPointFp.prototype.add = pointFpAdd; ECPointFp.prototype.twice = pointFpTwice; ECPointFp.prototype.multiply = pointFpMultiply; ECPointFp.prototype.multiplyTwo = pointFpMultiplyTwo; function ECCurveFp(e, d, c) { this.q = e; this.a = this.fromBigInteger(d); this.b = this.fromBigInteger(c); this.infinity = new ECPointFp(this, null, null) } function curveFpGetQ() { return this.q } function curveFpGetA() { return this.a } function curveFpGetB() { return this.b } function curveFpEquals(a) { if (a == this) { return true } return (this.q.equals(a.q) && this.a.equals(a.a) && this.b.equals(a.b)) } function curveFpGetInfinity() { return this.infinity } function curveFpFromBigInteger(a) { return new ECFieldElementFp(this.q, a) } function curveFpDecodePointHex(d) { switch (parseInt(d.substr(0, 2), 16)) { case 0: return this.infinity; case 2: case 3: return null; case 4: case 6: case 7: var a = (d.length - 2) / 2; var c = d.substr(2, a); var b = d.substr(a + 2, a); return new ECPointFp(this, this.fromBigInteger(new BigInteger(c, 16)), this.fromBigInteger(new BigInteger(b, 16))); default: return null } } ECCurveFp.prototype.getQ = curveFpGetQ; ECCurveFp.prototype.getA = curveFpGetA; ECCurveFp.prototype.getB = curveFpGetB; ECCurveFp.prototype.equals = curveFpEquals; ECCurveFp.prototype.getInfinity = curveFpGetInfinity; ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex;\n/*! (c) Stefan Thomas | https://github.com/bitcoinjs/bitcoinjs-lib\n */\nECFieldElementFp.prototype.getByteLength = function () { return Math.floor((this.toBigInteger().bitLength() + 7) / 8) }; ECPointFp.prototype.getEncoded = function (c) { var d = function (h, f) { var g = h.toByteArrayUnsigned(); if (f < g.length) { g = g.slice(g.length - f) } else { while (f > g.length) { g.unshift(0) } } return g }; var a = this.getX().toBigInteger(); var e = this.getY().toBigInteger(); var b = d(a, 32); if (c) { if (e.isEven()) { b.unshift(2) } else { b.unshift(3) } } else { b.unshift(4); b = b.concat(d(e, 32)) } return b }; ECPointFp.decodeFrom = function (g, c) { var f = c[0]; var e = c.length - 1; var d = c.slice(1, 1 + e / 2); var b = c.slice(1 + e / 2, 1 + e); d.unshift(0); b.unshift(0); var a = new BigInteger(d); var h = new BigInteger(b); return new ECPointFp(g, g.fromBigInteger(a), g.fromBigInteger(h)) }; ECPointFp.decodeFromHex = function (g, c) { var f = c.substr(0, 2); var e = c.length - 2; var d = c.substr(2, e / 2); var b = c.substr(2 + e / 2, e / 2); var a = new BigInteger(d, 16); var h = new BigInteger(b, 16); return new ECPointFp(g, g.fromBigInteger(a), g.fromBigInteger(h)) }; ECPointFp.prototype.add2D = function (c) { if (this.isInfinity()) { return c } if (c.isInfinity()) { return this } if (this.x.equals(c.x)) { if (this.y.equals(c.y)) { return this.twice() } return this.curve.getInfinity() } var g = c.x.subtract(this.x); var e = c.y.subtract(this.y); var a = e.divide(g); var d = a.square().subtract(this.x).subtract(c.x); var f = a.multiply(this.x.subtract(d)).subtract(this.y); return new ECPointFp(this.curve, d, f) }; ECPointFp.prototype.twice2D = function () { if (this.isInfinity()) { return this } if (this.y.toBigInteger().signum() == 0) { return this.curve.getInfinity() } var b = this.curve.fromBigInteger(BigInteger.valueOf(2)); var e = this.curve.fromBigInteger(BigInteger.valueOf(3)); var a = this.x.square().multiply(e).add(this.curve.a).divide(this.y.multiply(b)); var c = a.square().subtract(this.x.multiply(b)); var d = a.multiply(this.x.subtract(c)).subtract(this.y); return new ECPointFp(this.curve, c, d) }; ECPointFp.prototype.multiply2D = function (b) { if (this.isInfinity()) { return this } if (b.signum() == 0) { return this.curve.getInfinity() } var g = b; var f = g.multiply(new BigInteger(\"3\")); var l = this.negate(); var d = this; var c; for (c = f.bitLength() - 2; c > 0; --c) { d = d.twice(); var a = f.testBit(c); var j = g.testBit(c); if (a != j) { d = d.add2D(a ? this : l) } } return d }; ECPointFp.prototype.isOnCurve = function () { var d = this.getX().toBigInteger(); var i = this.getY().toBigInteger(); var f = this.curve.getA().toBigInteger(); var c = this.curve.getB().toBigInteger(); var h = this.curve.getQ(); var e = i.multiply(i).mod(h); var g = d.multiply(d).multiply(d).add(f.multiply(d)).add(c).mod(h); return e.equals(g) }; ECPointFp.prototype.toString = function () { return \"(\" + this.getX().toBigInteger().toString() + \",\" + this.getY().toBigInteger().toString() + \")\" }; ECPointFp.prototype.validate = function () { var c = this.curve.getQ(); if (this.isInfinity()) { throw new Error(\"Point is at infinity.\") } var a = this.getX().toBigInteger(); var b = this.getY().toBigInteger(); if (a.compareTo(BigInteger.ONE) < 0 || a.compareTo(c.subtract(BigInteger.ONE)) > 0) { throw new Error(\"x coordinate out of bounds\") } if (b.compareTo(BigInteger.ONE) < 0 || b.compareTo(c.subtract(BigInteger.ONE)) > 0) { throw new Error(\"y coordinate out of bounds\") } if (!this.isOnCurve()) { throw new Error(\"Point is not on the curve.\") } if (this.multiply(c).isInfinity()) { throw new Error(\"Point is not a scalar multiple of G.\") } return true };\n/*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval\n */\nvar jsonParse = (function () { var e = \"(?:-?\\\\b(?:0|[1-9][0-9]*)(?:\\\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\\\b)\"; var j = '(?:[^\\\\0-\\\\x08\\\\x0a-\\\\x1f\"\\\\\\\\]|\\\\\\\\(?:[\"/\\\\\\\\bfnrt]|u[0-9A-Fa-f]{4}))'; var i = '(?:\"' + j + '*\")'; var d = new RegExp(\"(?:false|true|null|[\\\\{\\\\}\\\\[\\\\]]|\" + e + \"|\" + i + \")\", \"g\"); var k = new RegExp(\"\\\\\\\\(?:([^u])|u(.{4}))\", \"g\"); var g = { '\"': '\"', \"/\": \"/\", \"\\\\\": \"\\\\\", b: \"\\b\", f: \"\\f\", n: \"\\n\", r: \"\\r\", t: \"\\t\" }; function h(l, m, n) { return m ? g[m] : String.fromCharCode(parseInt(n, 16)) } var c = new String(\"\"); var a = \"\\\\\"; var f = { \"{\": Object, \"[\": Array }; var b = Object.hasOwnProperty; return function (u, q) { var p = u.match(d); var x; var v = p[0]; var l = false; if (\"{\" === v) { x = {} } else { if (\"[\" === v) { x = [] } else { x = []; l = true } } var t; var r = [x]; for (var o = 1 - l, m = p.length; o < m; ++o) { v = p[o]; var w; switch (v.charCodeAt(0)) { default: w = r[0]; w[t || w.length] = +(v); t = void 0; break; case 34: v = v.substring(1, v.length - 1); if (v.indexOf(a) !== -1) { v = v.replace(k, h) } w = r[0]; if (!t) { if (w instanceof Array) { t = w.length } else { t = v || c; break } } w[t] = v; t = void 0; break; case 91: w = r[0]; r.unshift(w[t || w.length] = []); t = void 0; break; case 93: r.shift(); break; case 102: w = r[0]; w[t || w.length] = false; t = void 0; break; case 110: w = r[0]; w[t || w.length] = null; t = void 0; break; case 116: w = r[0]; w[t || w.length] = true; t = void 0; break; case 123: w = r[0]; r.unshift(w[t || w.length] = {}); t = void 0; break; case 125: r.shift(); break } } if (l) { if (r.length !== 1) { throw new Error() } x = x[0] } else { if (r.length) { throw new Error() } } if (q) { var s = function (C, B) { var D = C[B]; if (D && typeof D === \"object\") { var n = null; for (var z in D) { if (b.call(D, z) && D !== C) { var y = s(D, z); if (y !== void 0) { D[z] = y } else { if (!n) { n = [] } n.push(z) } } } if (n) { for (var A = n.length; --A >= 0;) { delete D[n[A]] } } } return q.call(C, B, D) }; x = s({ \"\": x }, \"\") } return x } })();\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1) { KJUR.asn1 = {} } KJUR.asn1.ASN1Util = new function () { this.integerToByteHex = function (a) { var b = a.toString(16); if ((b.length % 2) == 1) { b = \"0\" + b } return b }; this.bigIntToMinTwosComplementsHex = function (j) { var f = j.toString(16); if (f.substr(0, 1) != \"-\") { if (f.length % 2 == 1) { f = \"0\" + f } else { if (!f.match(/^[0-7]/)) { f = \"00\" + f } } } else { var a = f.substr(1); var e = a.length; if (e % 2 == 1) { e += 1 } else { if (!f.match(/^[0-7]/)) { e += 2 } } var g = \"\"; for (var d = 0; d < e; d++) { g += \"f\" } var c = new BigInteger(g, 16); var b = c.xor(j).add(BigInteger.ONE); f = b.toString(16).replace(/^-/, \"\") } return f }; this.getPEMStringFromHex = function (a, b) { return hextopem(a, b) }; this.newObject = function (k) { var D = KJUR, n = D.asn1, z = n.DERBoolean, e = n.DERInteger, s = n.DERBitString, h = n.DEROctetString, v = n.DERNull, w = n.DERObjectIdentifier, l = n.DEREnumerated, g = n.DERUTF8String, f = n.DERNumericString, y = n.DERPrintableString, u = n.DERTeletexString, p = n.DERIA5String, C = n.DERUTCTime, j = n.DERGeneralizedTime, m = n.DERSequence, c = n.DERSet, r = n.DERTaggedObject, o = n.ASN1Util.newObject; var t = Object.keys(k); if (t.length != 1) { throw \"key of param shall be only one.\" } var F = t[0]; if (\":bool:int:bitstr:octstr:null:oid:enum:utf8str:numstr:prnstr:telstr:ia5str:utctime:gentime:seq:set:tag:\".indexOf(\":\" + F + \":\") == -1) { throw \"undefined key: \" + F } if (F == \"bool\") { return new z(k[F]) } if (F == \"int\") { return new e(k[F]) } if (F == \"bitstr\") { return new s(k[F]) } if (F == \"octstr\") { return new h(k[F]) } if (F == \"null\") { return new v(k[F]) } if (F == \"oid\") { return new w(k[F]) } if (F == \"enum\") { return new l(k[F]) } if (F == \"utf8str\") { return new g(k[F]) } if (F == \"numstr\") { return new f(k[F]) } if (F == \"prnstr\") { return new y(k[F]) } if (F == \"telstr\") { return new u(k[F]) } if (F == \"ia5str\") { return new p(k[F]) } if (F == \"utctime\") { return new C(k[F]) } if (F == \"gentime\") { return new j(k[F]) } if (F == \"seq\") { var d = k[F]; var E = []; for (var x = 0; x < d.length; x++) { var B = o(d[x]); E.push(B) } return new m({ array: E }) } if (F == \"set\") { var d = k[F]; var E = []; for (var x = 0; x < d.length; x++) { var B = o(d[x]); E.push(B) } return new c({ array: E }) } if (F == \"tag\") { var A = k[F]; if (Object.prototype.toString.call(A) === \"[object Array]\" && A.length == 3) { var q = o(A[2]); return new r({ tag: A[0], explicit: A[1], obj: q }) } else { var b = {}; if (A.explicit !== undefined) { b.explicit = A.explicit } if (A.tag !== undefined) { b.tag = A.tag } if (A.obj === undefined) { throw \"obj shall be specified for 'tag'.\" } b.obj = o(A.obj); return new r(b) } } }; this.jsonToASN1HEX = function (b) { var a = this.newObject(b); return a.getEncodedHex() } }; KJUR.asn1.ASN1Util.oidHexToInt = function (a) { var j = \"\"; var k = parseInt(a.substr(0, 2), 16); var d = Math.floor(k / 40); var c = k % 40; var j = d + \".\" + c; var e = \"\"; for (var f = 2; f < a.length; f += 2) { var g = parseInt(a.substr(f, 2), 16); var h = (\"00000000\" + g.toString(2)).slice(-8); e = e + h.substr(1, 7); if (h.substr(0, 1) == \"0\") { var b = new BigInteger(e, 2); j = j + \".\" + b.toString(10); e = \"\" } } return j }; KJUR.asn1.ASN1Util.oidIntToHex = function (f) { var e = function (a) { var k = a.toString(16); if (k.length == 1) { k = \"0\" + k } return k }; var d = function (o) { var n = \"\"; var k = new BigInteger(o, 10); var a = k.toString(2); var l = 7 - a.length % 7; if (l == 7) { l = 0 } var q = \"\"; for (var m = 0; m < l; m++) { q += \"0\" } a = q + a; for (var m = 0; m < a.length - 1; m += 7) { var p = a.substr(m, 7); if (m != a.length - 7) { p = \"1\" + p } n += e(parseInt(p, 2)) } return n }; if (!f.match(/^[0-9.]+$/)) { throw \"malformed oid string: \" + f } var g = \"\"; var b = f.split(\".\"); var j = parseInt(b[0]) * 40 + parseInt(b[1]); g += e(j); b.splice(0, 2); for (var c = 0; c < b.length; c++) { g += d(b[c]) } return g }; KJUR.asn1.ASN1Object = function () { var c = true; var b = null; var d = \"00\"; var e = \"00\"; var a = \"\"; this.getLengthHexFromValue = function () { if (typeof this.hV == \"undefined\" || this.hV == null) { throw \"this.hV is null or undefined.\" } if (this.hV.length % 2 == 1) { throw \"value hex must be even length: n=\" + a.length + \",v=\" + this.hV } var i = this.hV.length / 2; var h = i.toString(16); if (h.length % 2 == 1) { h = \"0\" + h } if (i < 128) { return h } else { var g = h.length / 2; if (g > 15) { throw \"ASN.1 length too long to represent by 8x: n = \" + i.toString(16) } var f = 128 + g; return f.toString(16) + h } }; this.getEncodedHex = function () { if (this.hTLV == null || this.isModified) { this.hV = this.getFreshValueHex(); this.hL = this.getLengthHexFromValue(); this.hTLV = this.hT + this.hL + this.hV; this.isModified = false } return this.hTLV }; this.getValueHex = function () { this.getEncodedHex(); return this.hV }; this.getFreshValueHex = function () { return \"\" } }; KJUR.asn1.DERAbstractString = function (c) { KJUR.asn1.DERAbstractString.superclass.constructor.call(this); var b = null; var a = null; this.getString = function () { return this.s }; this.setString = function (d) { this.hTLV = null; this.isModified = true; this.s = d; this.hV = stohex(this.s) }; this.setStringHex = function (d) { this.hTLV = null; this.isModified = true; this.s = null; this.hV = d }; this.getFreshValueHex = function () { return this.hV }; if (typeof c != \"undefined\") { if (typeof c == \"string\") { this.setString(c) } else { if (typeof c.str != \"undefined\") { this.setString(c.str) } else { if (typeof c.hex != \"undefined\") { this.setStringHex(c.hex) } } } } }; YAHOO.lang.extend(KJUR.asn1.DERAbstractString, KJUR.asn1.ASN1Object); KJUR.asn1.DERAbstractTime = function (c) { KJUR.asn1.DERAbstractTime.superclass.constructor.call(this); var b = null; var a = null; this.localDateToUTC = function (f) { utc = f.getTime() + (f.getTimezoneOffset() * 60000); var e = new Date(utc); return e }; this.formatDate = function (m, o, e) { var g = this.zeroPadding; var n = this.localDateToUTC(m); var p = String(n.getFullYear()); if (o == \"utc\") { p = p.substr(2, 2) } var l = g(String(n.getMonth() + 1), 2); var q = g(String(n.getDate()), 2); var h = g(String(n.getHours()), 2); var i = g(String(n.getMinutes()), 2); var j = g(String(n.getSeconds()), 2); var r = p + l + q + h + i + j; if (e === true) { var f = n.getMilliseconds(); if (f != 0) { var k = g(String(f), 3); k = k.replace(/[0]+$/, \"\"); r = r + \".\" + k } } return r + \"Z\" }; this.zeroPadding = function (e, d) { if (e.length >= d) { return e } return new Array(d - e.length + 1).join(\"0\") + e }; this.getString = function () { return this.s }; this.setString = function (d) { this.hTLV = null; this.isModified = true; this.s = d; this.hV = stohex(d) }; this.setByDateValue = function (h, j, e, d, f, g) { var i = new Date(Date.UTC(h, j - 1, e, d, f, g, 0)); this.setByDate(i) }; this.getFreshValueHex = function () { return this.hV } }; YAHOO.lang.extend(KJUR.asn1.DERAbstractTime, KJUR.asn1.ASN1Object); KJUR.asn1.DERAbstractStructured = function (b) { KJUR.asn1.DERAbstractString.superclass.constructor.call(this); var a = null; this.setByASN1ObjectArray = function (c) { this.hTLV = null; this.isModified = true; this.asn1Array = c }; this.appendASN1Object = function (c) { this.hTLV = null; this.isModified = true; this.asn1Array.push(c) }; this.asn1Array = new Array(); if (typeof b != \"undefined\") { if (typeof b.array != \"undefined\") { this.asn1Array = b.array } } }; YAHOO.lang.extend(KJUR.asn1.DERAbstractStructured, KJUR.asn1.ASN1Object); KJUR.asn1.DERBoolean = function () { KJUR.asn1.DERBoolean.superclass.constructor.call(this); this.hT = \"01\"; this.hTLV = \"0101ff\" }; YAHOO.lang.extend(KJUR.asn1.DERBoolean, KJUR.asn1.ASN1Object); KJUR.asn1.DERInteger = function (a) { KJUR.asn1.DERInteger.superclass.constructor.call(this); this.hT = \"02\"; this.setByBigInteger = function (b) { this.hTLV = null; this.isModified = true; this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(b) }; this.setByInteger = function (c) { var b = new BigInteger(String(c), 10); this.setByBigInteger(b) }; this.setValueHex = function (b) { this.hV = b }; this.getFreshValueHex = function () { return this.hV }; if (typeof a != \"undefined\") { if (typeof a.bigint != \"undefined\") { this.setByBigInteger(a.bigint) } else { if (typeof a[\"int\"] != \"undefined\") { this.setByInteger(a[\"int\"]) } else { if (typeof a == \"number\") { this.setByInteger(a) } else { if (typeof a.hex != \"undefined\") { this.setValueHex(a.hex) } } } } } }; YAHOO.lang.extend(KJUR.asn1.DERInteger, KJUR.asn1.ASN1Object); KJUR.asn1.DERBitString = function (b) { if (b !== undefined && typeof b.obj !== \"undefined\") { var a = KJUR.asn1.ASN1Util.newObject(b.obj); b.hex = \"00\" + a.getEncodedHex() } KJUR.asn1.DERBitString.superclass.constructor.call(this); this.hT = \"03\"; this.setHexValueIncludingUnusedBits = function (c) { this.hTLV = null; this.isModified = true; this.hV = c }; this.setUnusedBitsAndHexValue = function (c, e) { if (c < 0 || 7 < c) { throw \"unused bits shall be from 0 to 7: u = \" + c } var d = \"0\" + c; this.hTLV = null; this.isModified = true; this.hV = d + e }; this.setByBinaryString = function (e) { e = e.replace(/0+$/, \"\"); var f = 8 - e.length % 8; if (f == 8) { f = 0 } for (var g = 0; g <= f; g++) { e += \"0\" } var j = \"\"; for (var g = 0; g < e.length - 1; g += 8) { var d = e.substr(g, 8); var c = parseInt(d, 2).toString(16); if (c.length == 1) { c = \"0\" + c } j += c } this.hTLV = null; this.isModified = true; this.hV = \"0\" + f + j }; this.setByBooleanArray = function (e) { var d = \"\"; for (var c = 0; c < e.length; c++) { if (e[c] == true) { d += \"1\" } else { d += \"0\" } } this.setByBinaryString(d) }; this.newFalseArray = function (e) { var c = new Array(e); for (var d = 0; d < e; d++) { c[d] = false } return c }; this.getFreshValueHex = function () { return this.hV }; if (typeof b != \"undefined\") { if (typeof b == \"string\" && b.toLowerCase().match(/^[0-9a-f]+$/)) { this.setHexValueIncludingUnusedBits(b) } else { if (typeof b.hex != \"undefined\") { this.setHexValueIncludingUnusedBits(b.hex) } else { if (typeof b.bin != \"undefined\") { this.setByBinaryString(b.bin) } else { if (typeof b.array != \"undefined\") { this.setByBooleanArray(b.array) } } } } } }; YAHOO.lang.extend(KJUR.asn1.DERBitString, KJUR.asn1.ASN1Object); KJUR.asn1.DEROctetString = function (b) { if (b !== undefined && typeof b.obj !== \"undefined\") { var a = KJUR.asn1.ASN1Util.newObject(b.obj); b.hex = a.getEncodedHex() } KJUR.asn1.DEROctetString.superclass.constructor.call(this, b); this.hT = \"04\" }; YAHOO.lang.extend(KJUR.asn1.DEROctetString, KJUR.asn1.DERAbstractString); KJUR.asn1.DERNull = function () { KJUR.asn1.DERNull.superclass.constructor.call(this); this.hT = \"05\"; this.hTLV = \"0500\" }; YAHOO.lang.extend(KJUR.asn1.DERNull, KJUR.asn1.ASN1Object); KJUR.asn1.DERObjectIdentifier = function (c) { var b = function (d) { var e = d.toString(16); if (e.length == 1) { e = \"0\" + e } return e }; var a = function (k) { var j = \"\"; var e = new BigInteger(k, 10); var d = e.toString(2); var f = 7 - d.length % 7; if (f == 7) { f = 0 } var m = \"\"; for (var g = 0; g < f; g++) { m += \"0\" } d = m + d; for (var g = 0; g < d.length - 1; g += 7) { var l = d.substr(g, 7); if (g != d.length - 7) { l = \"1\" + l } j += b(parseInt(l, 2)) } return j }; KJUR.asn1.DERObjectIdentifier.superclass.constructor.call(this); this.hT = \"06\"; this.setValueHex = function (d) { this.hTLV = null; this.isModified = true; this.s = null; this.hV = d }; this.setValueOidString = function (f) { if (!f.match(/^[0-9.]+$/)) { throw \"malformed oid string: \" + f } var g = \"\"; var d = f.split(\".\"); var j = parseInt(d[0]) * 40 + parseInt(d[1]); g += b(j); d.splice(0, 2); for (var e = 0; e < d.length; e++) { g += a(d[e]) } this.hTLV = null; this.isModified = true; this.s = null; this.hV = g }; this.setValueName = function (e) { var d = KJUR.asn1.x509.OID.name2oid(e); if (d !== \"\") { this.setValueOidString(d) } else { throw \"DERObjectIdentifier oidName undefined: \" + e } }; this.getFreshValueHex = function () { return this.hV }; if (c !== undefined) { if (typeof c === \"string\") { if (c.match(/^[0-2].[0-9.]+$/)) { this.setValueOidString(c) } else { this.setValueName(c) } } else { if (c.oid !== undefined) { this.setValueOidString(c.oid) } else { if (c.hex !== undefined) { this.setValueHex(c.hex) } else { if (c.name !== undefined) { this.setValueName(c.name) } } } } } }; YAHOO.lang.extend(KJUR.asn1.DERObjectIdentifier, KJUR.asn1.ASN1Object); KJUR.asn1.DEREnumerated = function (a) { KJUR.asn1.DEREnumerated.superclass.constructor.call(this); this.hT = \"0a\"; this.setByBigInteger = function (b) { this.hTLV = null; this.isModified = true; this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(b) }; this.setByInteger = function (c) { var b = new BigInteger(String(c), 10); this.setByBigInteger(b) }; this.setValueHex = function (b) { this.hV = b }; this.getFreshValueHex = function () { return this.hV }; if (typeof a != \"undefined\") { if (typeof a[\"int\"] != \"undefined\") { this.setByInteger(a[\"int\"]) } else { if (typeof a == \"number\") { this.setByInteger(a) } else { if (typeof a.hex != \"undefined\") { this.setValueHex(a.hex) } } } } }; YAHOO.lang.extend(KJUR.asn1.DEREnumerated, KJUR.asn1.ASN1Object); KJUR.asn1.DERUTF8String = function (a) { KJUR.asn1.DERUTF8String.superclass.constructor.call(this, a); this.hT = \"0c\" }; YAHOO.lang.extend(KJUR.asn1.DERUTF8String, KJUR.asn1.DERAbstractString); KJUR.asn1.DERNumericString = function (a) { KJUR.asn1.DERNumericString.superclass.constructor.call(this, a); this.hT = \"12\" }; YAHOO.lang.extend(KJUR.asn1.DERNumericString, KJUR.asn1.DERAbstractString); KJUR.asn1.DERPrintableString = function (a) { KJUR.asn1.DERPrintableString.superclass.constructor.call(this, a); this.hT = \"13\" }; YAHOO.lang.extend(KJUR.asn1.DERPrintableString, KJUR.asn1.DERAbstractString); KJUR.asn1.DERTeletexString = function (a) { KJUR.asn1.DERTeletexString.superclass.constructor.call(this, a); this.hT = \"14\" }; YAHOO.lang.extend(KJUR.asn1.DERTeletexString, KJUR.asn1.DERAbstractString); KJUR.asn1.DERIA5String = function (a) { KJUR.asn1.DERIA5String.superclass.constructor.call(this, a); this.hT = \"16\" }; YAHOO.lang.extend(KJUR.asn1.DERIA5String, KJUR.asn1.DERAbstractString); KJUR.asn1.DERUTCTime = function (a) { KJUR.asn1.DERUTCTime.superclass.constructor.call(this, a); this.hT = \"17\"; this.setByDate = function (b) { this.hTLV = null; this.isModified = true; this.date = b; this.s = this.formatDate(this.date, \"utc\"); this.hV = stohex(this.s) }; this.getFreshValueHex = function () { if (typeof this.date == \"undefined\" && typeof this.s == \"undefined\") { this.date = new Date(); this.s = this.formatDate(this.date, \"utc\"); this.hV = stohex(this.s) } return this.hV }; if (a !== undefined) { if (a.str !== undefined) { this.setString(a.str) } else { if (typeof a == \"string\" && a.match(/^[0-9]{12}Z$/)) { this.setString(a) } else { if (a.hex !== undefined) { this.setStringHex(a.hex) } else { if (a.date !== undefined) { this.setByDate(a.date) } } } } } }; YAHOO.lang.extend(KJUR.asn1.DERUTCTime, KJUR.asn1.DERAbstractTime); KJUR.asn1.DERGeneralizedTime = function (a) { KJUR.asn1.DERGeneralizedTime.superclass.constructor.call(this, a); this.hT = \"18\"; this.withMillis = false; this.setByDate = function (b) { this.hTLV = null; this.isModified = true; this.date = b; this.s = this.formatDate(this.date, \"gen\", this.withMillis); this.hV = stohex(this.s) }; this.getFreshValueHex = function () { if (this.date === undefined && this.s === undefined) { this.date = new Date(); this.s = this.formatDate(this.date, \"gen\", this.withMillis); this.hV = stohex(this.s) } return this.hV }; if (a !== undefined) { if (a.str !== undefined) { this.setString(a.str) } else { if (typeof a == \"string\" && a.match(/^[0-9]{14}Z$/)) { this.setString(a) } else { if (a.hex !== undefined) { this.setStringHex(a.hex) } else { if (a.date !== undefined) { this.setByDate(a.date) } } } } if (a.millis === true) { this.withMillis = true } } }; YAHOO.lang.extend(KJUR.asn1.DERGeneralizedTime, KJUR.asn1.DERAbstractTime); KJUR.asn1.DERSequence = function (a) { KJUR.asn1.DERSequence.superclass.constructor.call(this, a); this.hT = \"30\"; this.getFreshValueHex = function () { var c = \"\"; for (var b = 0; b < this.asn1Array.length; b++) { var d = this.asn1Array[b]; c += d.getEncodedHex() } this.hV = c; return this.hV } }; YAHOO.lang.extend(KJUR.asn1.DERSequence, KJUR.asn1.DERAbstractStructured); KJUR.asn1.DERSet = function (a) { KJUR.asn1.DERSet.superclass.constructor.call(this, a); this.hT = \"31\"; this.sortFlag = true; this.getFreshValueHex = function () { var b = new Array(); for (var c = 0; c < this.asn1Array.length; c++) { var d = this.asn1Array[c]; b.push(d.getEncodedHex()) } if (this.sortFlag == true) { b.sort() } this.hV = b.join(\"\"); return this.hV }; if (typeof a != \"undefined\") { if (typeof a.sortflag != \"undefined\" && a.sortflag == false) { this.sortFlag = false } } }; YAHOO.lang.extend(KJUR.asn1.DERSet, KJUR.asn1.DERAbstractStructured); KJUR.asn1.DERTaggedObject = function (a) { KJUR.asn1.DERTaggedObject.superclass.constructor.call(this); this.hT = \"a0\"; this.hV = \"\"; this.isExplicit = true; this.asn1Object = null; this.setASN1Object = function (b, c, d) { this.hT = c; this.isExplicit = b; this.asn1Object = d; if (this.isExplicit) { this.hV = this.asn1Object.getEncodedHex(); this.hTLV = null; this.isModified = true } else { this.hV = null; this.hTLV = d.getEncodedHex(); this.hTLV = this.hTLV.replace(/^../, c); this.isModified = false } }; this.getFreshValueHex = function () { return this.hV }; if (typeof a != \"undefined\") { if (typeof a.tag != \"undefined\") { this.hT = a.tag } if (typeof a.explicit != \"undefined\") { this.isExplicit = a.explicit } if (typeof a.obj != \"undefined\") { this.asn1Object = a.obj; this.setASN1Object(this.isExplicit, this.hT, this.asn1Object) } } }; YAHOO.lang.extend(KJUR.asn1.DERTaggedObject, KJUR.asn1.ASN1Object);\nvar ASN1HEX = new function () { }; ASN1HEX.getLblen = function (c, a) { if (c.substr(a + 2, 1) != \"8\") { return 1 } var b = parseInt(c.substr(a + 3, 1)); if (b == 0) { return -1 } if (0 < b && b < 10) { return b + 1 } return -2 }; ASN1HEX.getL = function (c, b) { var a = ASN1HEX.getLblen(c, b); if (a < 1) { return \"\" } return c.substr(b + 2, a * 2) }; ASN1HEX.getVblen = function (d, a) { var c, b; c = ASN1HEX.getL(d, a); if (c == \"\") { return -1 } if (c.substr(0, 1) === \"8\") { b = new BigInteger(c.substr(2), 16) } else { b = new BigInteger(c, 16) } return b.intValue() }; ASN1HEX.getVidx = function (c, b) { var a = ASN1HEX.getLblen(c, b); if (a < 0) { return a } return b + (a + 1) * 2 }; ASN1HEX.getV = function (d, a) { var c = ASN1HEX.getVidx(d, a); var b = ASN1HEX.getVblen(d, a); return d.substr(c, b * 2) }; ASN1HEX.getTLV = function (b, a) { return b.substr(a, 2) + ASN1HEX.getL(b, a) + ASN1HEX.getV(b, a) }; ASN1HEX.getNextSiblingIdx = function (d, a) { var c = ASN1HEX.getVidx(d, a); var b = ASN1HEX.getVblen(d, a); return c + b * 2 }; ASN1HEX.getChildIdx = function (e, f) { var j = ASN1HEX; var g = new Array(); var i = j.getVidx(e, f); if (e.substr(f, 2) == \"03\") { g.push(i + 2) } else { g.push(i) } var l = j.getVblen(e, f); var c = i; var d = 0; while (1) { var b = j.getNextSiblingIdx(e, c); if (b == null || (b - i >= (l * 2))) { break } if (d >= 200) { break } g.push(b); c = b; d++ } return g }; ASN1HEX.getNthChildIdx = function (d, b, e) { var c = ASN1HEX.getChildIdx(d, b); return c[e] }; ASN1HEX.getIdxbyList = function (e, d, c, i) { var g = ASN1HEX; var f, b; if (c.length == 0) { if (i !== undefined) { if (e.substr(d, 2) !== i) { throw \"checking tag doesn't match: \" + e.substr(d, 2) + \"!=\" + i } } return d } f = c.shift(); b = g.getChildIdx(e, d); return g.getIdxbyList(e, b[f], c, i) }; ASN1HEX.getTLVbyList = function (d, c, b, f) { var e = ASN1HEX; var a = e.getIdxbyList(d, c, b); if (a === undefined) { throw \"can't find nthList object\" } if (f !== undefined) { if (d.substr(a, 2) != f) { throw \"checking tag doesn't match: \" + d.substr(a, 2) + \"!=\" + f } } return e.getTLV(d, a) }; ASN1HEX.getVbyList = function (e, c, b, g, i) { var f = ASN1HEX; var a, d; a = f.getIdxbyList(e, c, b, g); if (a === undefined) { throw \"can't find nthList object\" } d = f.getV(e, a); if (i === true) { d = d.substr(2) } return d }; ASN1HEX.hextooidstr = function (e) { var h = function (b, a) { if (b.length >= a) { return b } return new Array(a - b.length + 1).join(\"0\") + b }; var l = []; var o = e.substr(0, 2); var f = parseInt(o, 16); l[0] = new String(Math.floor(f / 40)); l[1] = new String(f % 40); var m = e.substr(2); var k = []; for (var g = 0; g < m.length / 2; g++) { k.push(parseInt(m.substr(g * 2, 2), 16)) } var j = []; var d = \"\"; for (var g = 0; g < k.length; g++) { if (k[g] & 128) { d = d + h((k[g] & 127).toString(2), 7) } else { d = d + h((k[g] & 127).toString(2), 7); j.push(new String(parseInt(d, 2))); d = \"\" } } var n = l.join(\".\"); if (j.length > 0) { n = n + \".\" + j.join(\".\") } return n }; ASN1HEX.dump = function (t, c, l, g) { var p = ASN1HEX; var j = p.getV; var y = p.dump; var w = p.getChildIdx; var e = t; if (t instanceof KJUR.asn1.ASN1Object) { e = t.getEncodedHex() } var q = function (A, i) { if (A.length <= i * 2) { return A } else { var v = A.substr(0, i) + \"..(total \" + A.length / 2 + \"bytes)..\" + A.substr(A.length - i, i); return v } }; if (c === undefined) { c = { ommit_long_octet: 32 } } if (l === undefined) { l = 0 } if (g === undefined) { g = \"\" } var x = c.ommit_long_octet; if (e.substr(l, 2) == \"01\") { var h = j(e, l); if (h == \"00\") { return g + \"BOOLEAN FALSE\\n\" } else { return g + \"BOOLEAN TRUE\\n\" } } if (e.substr(l, 2) == \"02\") { var h = j(e, l); return g + \"INTEGER \" + q(h, x) + \"\\n\" } if (e.substr(l, 2) == \"03\") { var h = j(e, l); return g + \"BITSTRING \" + q(h, x) + \"\\n\" } if (e.substr(l, 2) == \"04\") { var h = j(e, l); if (p.isASN1HEX(h)) { var k = g + \"OCTETSTRING, encapsulates\\n\"; k = k + y(h, c, 0, g + \"  \"); return k } else { return g + \"OCTETSTRING \" + q(h, x) + \"\\n\" } } if (e.substr(l, 2) == \"05\") { return g + \"NULL\\n\" } if (e.substr(l, 2) == \"06\") { var m = j(e, l); var a = KJUR.asn1.ASN1Util.oidHexToInt(m); var o = KJUR.asn1.x509.OID.oid2name(a); var b = a.replace(/\\./g, \" \"); if (o != \"\") { return g + \"ObjectIdentifier \" + o + \" (\" + b + \")\\n\" } else { return g + \"ObjectIdentifier (\" + b + \")\\n\" } } if (e.substr(l, 2) == \"0c\") { return g + \"UTF8String '\" + hextoutf8(j(e, l)) + \"'\\n\" } if (e.substr(l, 2) == \"13\") { return g + \"PrintableString '\" + hextoutf8(j(e, l)) + \"'\\n\" } if (e.substr(l, 2) == \"14\") { return g + \"TeletexString '\" + hextoutf8(j(e, l)) + \"'\\n\" } if (e.substr(l, 2) == \"16\") { return g + \"IA5String '\" + hextoutf8(j(e, l)) + \"'\\n\" } if (e.substr(l, 2) == \"17\") { return g + \"UTCTime \" + hextoutf8(j(e, l)) + \"\\n\" } if (e.substr(l, 2) == \"18\") { return g + \"GeneralizedTime \" + hextoutf8(j(e, l)) + \"\\n\" } if (e.substr(l, 2) == \"30\") { if (e.substr(l, 4) == \"3000\") { return g + \"SEQUENCE {}\\n\" } var k = g + \"SEQUENCE\\n\"; var d = w(e, l); var f = c; if ((d.length == 2 || d.length == 3) && e.substr(d[0], 2) == \"06\" && e.substr(d[d.length - 1], 2) == \"04\") { var o = p.oidname(j(e, d[0])); var r = JSON.parse(JSON.stringify(c)); r.x509ExtName = o; f = r } for (var u = 0; u < d.length; u++) { k = k + y(e, f, d[u], g + \"  \") } return k } if (e.substr(l, 2) == \"31\") { var k = g + \"SET\\n\"; var d = w(e, l); for (var u = 0; u < d.length; u++) { k = k + y(e, c, d[u], g + \"  \") } return k } var z = parseInt(e.substr(l, 2), 16); if ((z & 128) != 0) { var n = z & 31; if ((z & 32) != 0) { var k = g + \"[\" + n + \"]\\n\"; var d = w(e, l); for (var u = 0; u < d.length; u++) { k = k + y(e, c, d[u], g + \"  \") } return k } else { var h = j(e, l); if (h.substr(0, 8) == \"68747470\") { h = hextoutf8(h) } if (c.x509ExtName === \"subjectAltName\" && n == 2) { h = hextoutf8(h) } var k = g + \"[\" + n + \"] \" + h + \"\\n\"; return k } } return g + \"UNKNOWN(\" + e.substr(l, 2) + \") \" + j(e, l) + \"\\n\" }; ASN1HEX.isASN1HEX = function (e) { var d = ASN1HEX; if (e.length % 2 == 1) { return false } var c = d.getVblen(e, 0); var b = e.substr(0, 2); var f = d.getL(e, 0); var a = e.length - b.length - f.length; if (a == c * 2) { return true } return false }; ASN1HEX.oidname = function (a) { var c = KJUR.asn1; if (KJUR.lang.String.isHex(a)) { a = c.ASN1Util.oidHexToInt(a) } var b = c.x509.OID.oid2name(a); if (b === \"\") { b = a } return b };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1) { KJUR.asn1 = {} } if (typeof KJUR.asn1.x509 == \"undefined\" || !KJUR.asn1.x509) { KJUR.asn1.x509 = {} } KJUR.asn1.x509.Certificate = function (e) { KJUR.asn1.x509.Certificate.superclass.constructor.call(this); var a = null, j = null, h = null, k = null, i = null, b = KJUR, f = b.crypto, g = b.asn1, d = g.DERSequence, c = g.DERBitString; this.sign = function () { this.asn1SignatureAlg = this.asn1TBSCert.asn1SignatureAlg; var m = new KJUR.crypto.Signature({ alg: this.asn1SignatureAlg.nameAlg }); m.init(this.prvKey); m.updateHex(this.asn1TBSCert.getEncodedHex()); this.hexSig = m.sign(); this.asn1Sig = new c({ hex: \"00\" + this.hexSig }); var l = new d({ array: [this.asn1TBSCert, this.asn1SignatureAlg, this.asn1Sig] }); this.hTLV = l.getEncodedHex(); this.isModified = false }; this.setSignatureHex = function (l) { this.asn1SignatureAlg = this.asn1TBSCert.asn1SignatureAlg; this.hexSig = l; this.asn1Sig = new c({ hex: \"00\" + this.hexSig }); var m = new d({ array: [this.asn1TBSCert, this.asn1SignatureAlg, this.asn1Sig] }); this.hTLV = m.getEncodedHex(); this.isModified = false }; this.getEncodedHex = function () { if (this.isModified == false && this.hTLV != null) { return this.hTLV } throw \"not signed yet\" }; this.getPEMString = function () { var l = hextob64nl(this.getEncodedHex()); return \"-----BEGIN CERTIFICATE-----\\r\\n\" + l + \"\\r\\n-----END CERTIFICATE-----\\r\\n\" }; if (e !== undefined) { if (e.tbscertobj !== undefined) { this.asn1TBSCert = e.tbscertobj } if (e.prvkeyobj !== undefined) { this.prvKey = e.prvkeyobj } } }; YAHOO.lang.extend(KJUR.asn1.x509.Certificate, KJUR.asn1.ASN1Object); KJUR.asn1.x509.TBSCertificate = function (e) { KJUR.asn1.x509.TBSCertificate.superclass.constructor.call(this); var b = KJUR, i = b.asn1, f = i.DERSequence, h = i.DERInteger, c = i.DERTaggedObject, d = i.x509, g = d.Time, a = d.X500Name, j = d.SubjectPublicKeyInfo; this._initialize = function () { this.asn1Array = new Array(); this.asn1Version = new c({ obj: new h({ \"int\": 2 }) }); this.asn1SerialNumber = null; this.asn1SignatureAlg = null; this.asn1Issuer = null; this.asn1NotBefore = null; this.asn1NotAfter = null; this.asn1Subject = null; this.asn1SubjPKey = null; this.extensionsArray = new Array() }; this.setSerialNumberByParam = function (k) { this.asn1SerialNumber = new h(k) }; this.setSignatureAlgByParam = function (k) { this.asn1SignatureAlg = new d.AlgorithmIdentifier(k) }; this.setIssuerByParam = function (k) { this.asn1Issuer = new a(k) }; this.setNotBeforeByParam = function (k) { this.asn1NotBefore = new g(k) }; this.setNotAfterByParam = function (k) { this.asn1NotAfter = new g(k) }; this.setSubjectByParam = function (k) { this.asn1Subject = new a(k) }; this.setSubjectPublicKey = function (k) { this.asn1SubjPKey = new j(k) }; this.setSubjectPublicKeyByGetKey = function (l) { var k = KEYUTIL.getKey(l); this.asn1SubjPKey = new j(k) }; this.appendExtension = function (k) { this.extensionsArray.push(k) }; this.appendExtensionByName = function (l, k) { KJUR.asn1.x509.Extension.appendByNameToArray(l, k, this.extensionsArray) }; this.getEncodedHex = function () { if (this.asn1NotBefore == null || this.asn1NotAfter == null) { throw \"notBefore and/or notAfter not set\" } var l = new f({ array: [this.asn1NotBefore, this.asn1NotAfter] }); this.asn1Array = new Array(); this.asn1Array.push(this.asn1Version); this.asn1Array.push(this.asn1SerialNumber); this.asn1Array.push(this.asn1SignatureAlg); this.asn1Array.push(this.asn1Issuer); this.asn1Array.push(l); this.asn1Array.push(this.asn1Subject); this.asn1Array.push(this.asn1SubjPKey); if (this.extensionsArray.length > 0) { var m = new f({ array: this.extensionsArray }); var k = new c({ explicit: true, tag: \"a3\", obj: m }); this.asn1Array.push(k) } var n = new f({ array: this.asn1Array }); this.hTLV = n.getEncodedHex(); this.isModified = false; return this.hTLV }; this._initialize() }; YAHOO.lang.extend(KJUR.asn1.x509.TBSCertificate, KJUR.asn1.ASN1Object); KJUR.asn1.x509.Extension = function (d) { KJUR.asn1.x509.Extension.superclass.constructor.call(this); var f = null, a = KJUR, e = a.asn1, h = e.DERObjectIdentifier, i = e.DEROctetString, b = e.DERBitString, g = e.DERBoolean, c = e.DERSequence; this.getEncodedHex = function () { var m = new h({ oid: this.oid }); var l = new i({ hex: this.getExtnValueHex() }); var k = new Array(); k.push(m); if (this.critical) { k.push(new g()) } k.push(l); var j = new c({ array: k }); return j.getEncodedHex() }; this.critical = false; if (typeof d != \"undefined\") { if (typeof d.critical != \"undefined\") { this.critical = d.critical } } }; YAHOO.lang.extend(KJUR.asn1.x509.Extension, KJUR.asn1.ASN1Object); KJUR.asn1.x509.Extension.appendByNameToArray = function (e, c, b) { var g = e.toLowerCase(), f = KJUR.asn1.x509; if (g == \"basicconstraints\") { var d = new f.BasicConstraints(c); b.push(d) } else { if (g == \"keyusage\") { var d = new f.KeyUsage(c); b.push(d) } else { if (g == \"crldistributionpoints\") { var d = new f.CRLDistributionPoints(c); b.push(d) } else { if (g == \"extkeyusage\") { var d = new f.ExtKeyUsage(c); b.push(d) } else { if (g == \"authoritykeyidentifier\") { var d = new f.AuthorityKeyIdentifier(c); b.push(d) } else { if (g == \"authorityinfoaccess\") { var d = new f.AuthorityInfoAccess(c); b.push(d) } else { if (g == \"subjectaltname\") { var d = new f.SubjectAltName(c); b.push(d) } else { if (g == \"issueraltname\") { var d = new f.IssuerAltName(c); b.push(d) } else { throw \"unsupported extension name: \" + e } } } } } } } } }; KJUR.asn1.x509.KeyUsage = function (a) { KJUR.asn1.x509.KeyUsage.superclass.constructor.call(this, a); this.getExtnValueHex = function () { return this.asn1ExtnValue.getEncodedHex() }; this.oid = \"2.5.29.15\"; if (typeof a != \"undefined\") { if (typeof a.bin != \"undefined\") { this.asn1ExtnValue = new KJUR.asn1.DERBitString(a) } } }; YAHOO.lang.extend(KJUR.asn1.x509.KeyUsage, KJUR.asn1.x509.Extension); KJUR.asn1.x509.BasicConstraints = function (c) { KJUR.asn1.x509.BasicConstraints.superclass.constructor.call(this, c); var a = false; var b = -1; this.getExtnValueHex = function () { var e = new Array(); if (this.cA) { e.push(new KJUR.asn1.DERBoolean()) } if (this.pathLen > -1) { e.push(new KJUR.asn1.DERInteger({ \"int\": this.pathLen })) } var d = new KJUR.asn1.DERSequence({ array: e }); this.asn1ExtnValue = d; return this.asn1ExtnValue.getEncodedHex() }; this.oid = \"2.5.29.19\"; this.cA = false; this.pathLen = -1; if (typeof c != \"undefined\") { if (typeof c.cA != \"undefined\") { this.cA = c.cA } if (typeof c.pathLen != \"undefined\") { this.pathLen = c.pathLen } } }; YAHOO.lang.extend(KJUR.asn1.x509.BasicConstraints, KJUR.asn1.x509.Extension); KJUR.asn1.x509.CRLDistributionPoints = function (d) { KJUR.asn1.x509.CRLDistributionPoints.superclass.constructor.call(this, d); var b = KJUR, a = b.asn1, c = a.x509; this.getExtnValueHex = function () { return this.asn1ExtnValue.getEncodedHex() }; this.setByDPArray = function (e) { this.asn1ExtnValue = new a.DERSequence({ array: e }) }; this.setByOneURI = function (h) { var e = new c.GeneralNames([{ uri: h }]); var g = new c.DistributionPointName(e); var f = new c.DistributionPoint({ dpobj: g }); this.setByDPArray([f]) }; this.oid = \"2.5.29.31\"; if (typeof d != \"undefined\") { if (typeof d.array != \"undefined\") { this.setByDPArray(d.array) } else { if (typeof d.uri != \"undefined\") { this.setByOneURI(d.uri) } } } }; YAHOO.lang.extend(KJUR.asn1.x509.CRLDistributionPoints, KJUR.asn1.x509.Extension); KJUR.asn1.x509.ExtKeyUsage = function (c) { KJUR.asn1.x509.ExtKeyUsage.superclass.constructor.call(this, c); var b = KJUR, a = b.asn1; this.setPurposeArray = function (d) { this.asn1ExtnValue = new a.DERSequence(); for (var e = 0; e < d.length; e++) { var f = new a.DERObjectIdentifier(d[e]); this.asn1ExtnValue.appendASN1Object(f) } }; this.getExtnValueHex = function () { return this.asn1ExtnValue.getEncodedHex() }; this.oid = \"2.5.29.37\"; if (typeof c != \"undefined\") { if (typeof c.array != \"undefined\") { this.setPurposeArray(c.array) } } }; YAHOO.lang.extend(KJUR.asn1.x509.ExtKeyUsage, KJUR.asn1.x509.Extension); KJUR.asn1.x509.AuthorityKeyIdentifier = function (d) { KJUR.asn1.x509.AuthorityKeyIdentifier.superclass.constructor.call(this, d); var b = KJUR, a = b.asn1, c = a.DERTaggedObject; this.asn1KID = null; this.asn1CertIssuer = null; this.asn1CertSN = null; this.getExtnValueHex = function () { var f = new Array(); if (this.asn1KID) { f.push(new c({ explicit: false, tag: \"80\", obj: this.asn1KID })) } if (this.asn1CertIssuer) { f.push(new c({ explicit: false, tag: \"a1\", obj: this.asn1CertIssuer })) } if (this.asn1CertSN) { f.push(new c({ explicit: false, tag: \"82\", obj: this.asn1CertSN })) } var e = new a.DERSequence({ array: f }); this.asn1ExtnValue = e; return this.asn1ExtnValue.getEncodedHex() }; this.setKIDByParam = function (e) { this.asn1KID = new KJUR.asn1.DEROctetString(e) }; this.setCertIssuerByParam = function (e) { this.asn1CertIssuer = new KJUR.asn1.x509.X500Name(e) }; this.setCertSNByParam = function (e) { this.asn1CertSN = new KJUR.asn1.DERInteger(e) }; this.oid = \"2.5.29.35\"; if (typeof d != \"undefined\") { if (typeof d.kid != \"undefined\") { this.setKIDByParam(d.kid) } if (typeof d.issuer != \"undefined\") { this.setCertIssuerByParam(d.issuer) } if (typeof d.sn != \"undefined\") { this.setCertSNByParam(d.sn) } } }; YAHOO.lang.extend(KJUR.asn1.x509.AuthorityKeyIdentifier, KJUR.asn1.x509.Extension); KJUR.asn1.x509.AuthorityInfoAccess = function (a) { KJUR.asn1.x509.AuthorityInfoAccess.superclass.constructor.call(this, a); this.setAccessDescriptionArray = function (k) { var j = new Array(), b = KJUR, g = b.asn1, d = g.DERSequence; for (var f = 0; f < k.length; f++) { var c = new g.DERObjectIdentifier(k[f].accessMethod); var e = new g.x509.GeneralName(k[f].accessLocation); var h = new d({ array: [c, e] }); j.push(h) } this.asn1ExtnValue = new d({ array: j }) }; this.getExtnValueHex = function () { return this.asn1ExtnValue.getEncodedHex() }; this.oid = \"1.3.6.1.5.5.7.1.1\"; if (typeof a != \"undefined\") { if (typeof a.array != \"undefined\") { this.setAccessDescriptionArray(a.array) } } }; YAHOO.lang.extend(KJUR.asn1.x509.AuthorityInfoAccess, KJUR.asn1.x509.Extension); KJUR.asn1.x509.SubjectAltName = function (a) { KJUR.asn1.x509.SubjectAltName.superclass.constructor.call(this, a); this.setNameArray = function (b) { this.asn1ExtnValue = new KJUR.asn1.x509.GeneralNames(b) }; this.getExtnValueHex = function () { return this.asn1ExtnValue.getEncodedHex() }; this.oid = \"2.5.29.17\"; if (a !== undefined) { if (a.array !== undefined) { this.setNameArray(a.array) } } }; YAHOO.lang.extend(KJUR.asn1.x509.SubjectAltName, KJUR.asn1.x509.Extension); KJUR.asn1.x509.IssuerAltName = function (a) { KJUR.asn1.x509.IssuerAltName.superclass.constructor.call(this, a); this.setNameArray = function (b) { this.asn1ExtnValue = new KJUR.asn1.x509.GeneralNames(b) }; this.getExtnValueHex = function () { return this.asn1ExtnValue.getEncodedHex() }; this.oid = \"2.5.29.18\"; if (a !== undefined) { if (a.array !== undefined) { this.setNameArray(a.array) } } }; YAHOO.lang.extend(KJUR.asn1.x509.IssuerAltName, KJUR.asn1.x509.Extension); KJUR.asn1.x509.CRL = function (f) { KJUR.asn1.x509.CRL.superclass.constructor.call(this); var b = null, d = null, e = null, c = null, a = null; this.sign = function () { this.asn1SignatureAlg = this.asn1TBSCertList.asn1SignatureAlg; sig = new KJUR.crypto.Signature({ alg: \"SHA1withRSA\", prov: \"cryptojs/jsrsa\" }); sig.initSign(this.prvKey); sig.updateHex(this.asn1TBSCertList.getEncodedHex()); this.hexSig = sig.sign(); this.asn1Sig = new KJUR.asn1.DERBitString({ hex: \"00\" + this.hexSig }); var g = new KJUR.asn1.DERSequence({ array: [this.asn1TBSCertList, this.asn1SignatureAlg, this.asn1Sig] }); this.hTLV = g.getEncodedHex(); this.isModified = false }; this.getEncodedHex = function () { if (this.isModified == false && this.hTLV != null) { return this.hTLV } throw \"not signed yet\" }; this.getPEMString = function () { var g = hextob64nl(this.getEncodedHex()); return \"-----BEGIN X509 CRL-----\\r\\n\" + g + \"\\r\\n-----END X509 CRL-----\\r\\n\" }; if (f !== undefined) { if (f.tbsobj !== undefined) { this.asn1TBSCertList = f.tbsobj } if (f.prvkeyobj !== undefined) { this.prvKey = f.prvkeyobj } } }; YAHOO.lang.extend(KJUR.asn1.x509.CRL, KJUR.asn1.ASN1Object); KJUR.asn1.x509.TBSCertList = function (g) { KJUR.asn1.x509.TBSCertList.superclass.constructor.call(this); var e = null, d = KJUR, c = d.asn1, b = c.DERSequence, f = c.x509, a = f.Time; this.setSignatureAlgByParam = function (h) { this.asn1SignatureAlg = new f.AlgorithmIdentifier(h) }; this.setIssuerByParam = function (h) { this.asn1Issuer = new f.X500Name(h) }; this.setThisUpdateByParam = function (h) { this.asn1ThisUpdate = new a(h) }; this.setNextUpdateByParam = function (h) { this.asn1NextUpdate = new a(h) }; this.addRevokedCert = function (h, i) { var k = {}; if (h != undefined && h != null) { k.sn = h } if (i != undefined && i != null) { k.time = i } var j = new f.CRLEntry(k); this.aRevokedCert.push(j) }; this.getEncodedHex = function () { this.asn1Array = new Array(); if (this.asn1Version != null) { this.asn1Array.push(this.asn1Version) } this.asn1Array.push(this.asn1SignatureAlg); this.asn1Array.push(this.asn1Issuer); this.asn1Array.push(this.asn1ThisUpdate); if (this.asn1NextUpdate != null) { this.asn1Array.push(this.asn1NextUpdate) } if (this.aRevokedCert.length > 0) { var h = new b({ array: this.aRevokedCert }); this.asn1Array.push(h) } var i = new b({ array: this.asn1Array }); this.hTLV = i.getEncodedHex(); this.isModified = false; return this.hTLV }; this._initialize = function () { this.asn1Version = null; this.asn1SignatureAlg = null; this.asn1Issuer = null; this.asn1ThisUpdate = null; this.asn1NextUpdate = null; this.aRevokedCert = new Array() }; this._initialize() }; YAHOO.lang.extend(KJUR.asn1.x509.TBSCertList, KJUR.asn1.ASN1Object); KJUR.asn1.x509.CRLEntry = function (e) { KJUR.asn1.x509.CRLEntry.superclass.constructor.call(this); var d = null, c = null, b = KJUR, a = b.asn1; this.setCertSerial = function (f) { this.sn = new a.DERInteger(f) }; this.setRevocationDate = function (f) { this.time = new a.x509.Time(f) }; this.getEncodedHex = function () { var f = new a.DERSequence({ array: [this.sn, this.time] }); this.TLV = f.getEncodedHex(); return this.TLV }; if (e !== undefined) { if (e.time !== undefined) { this.setRevocationDate(e.time) } if (e.sn !== undefined) { this.setCertSerial(e.sn) } } }; YAHOO.lang.extend(KJUR.asn1.x509.CRLEntry, KJUR.asn1.ASN1Object); KJUR.asn1.x509.X500Name = function (f) { KJUR.asn1.x509.X500Name.superclass.constructor.call(this); this.asn1Array = new Array(); var d = KJUR, c = d.asn1, e = c.x509, b = pemtohex; this.setByString = function (g) { var h = g.split(\"/\"); h.shift(); for (var j = 0; j < h.length; j++) { this.asn1Array.push(new e.RDN({ str: h[j] })) } }; this.setByLdapString = function (g) { var h = e.X500Name.ldapToOneline(g); this.setByString(h) }; this.setByObject = function (i) { for (var g in i) { if (i.hasOwnProperty(g)) { var h = new KJUR.asn1.x509.RDN({ str: g + \"=\" + i[g] }); this.asn1Array ? this.asn1Array.push(h) : this.asn1Array = [h] } } }; this.getEncodedHex = function () { if (typeof this.hTLV == \"string\") { return this.hTLV } var g = new c.DERSequence({ array: this.asn1Array }); this.hTLV = g.getEncodedHex(); return this.hTLV }; if (f !== undefined) { if (f.str !== undefined) { this.setByString(f.str) } else { if (f.ldapstr !== undefined) { this.setByLdapString(f.ldapstr) } else { if (typeof f === \"object\") { this.setByObject(f) } } } if (f.certissuer !== undefined) { var a = new X509(); a.hex = b(f.certissuer); this.hTLV = a.getIssuerHex() } if (f.certsubject !== undefined) { var a = new X509(); a.hex = b(f.certsubject); this.hTLV = a.getSubjectHex() } } }; YAHOO.lang.extend(KJUR.asn1.x509.X500Name, KJUR.asn1.ASN1Object); KJUR.asn1.x509.X500Name.onelineToLDAP = function (d) { if (d.substr(0, 1) !== \"/\") { throw \"malformed input\" } var b = \"\"; d = d.substr(1); var c = d.split(\"/\"); c.reverse(); c = c.map(function (a) { return a.replace(/,/, \"\\\\,\") }); return c.join(\",\") }; KJUR.asn1.x509.X500Name.ldapToOneline = function (g) { var c = g.split(\",\"); var e = false; var b = []; for (var f = 0; c.length > 0; f++) { var h = c.shift(); if (e === true) { var d = b.pop(); var j = (d + \",\" + h).replace(/\\\\,/g, \",\"); b.push(j); e = false } else { b.push(h) } if (h.substr(-1, 1) === \"\\\\\") { e = true } } b = b.map(function (a) { return a.replace(\"/\", \"\\\\/\") }); b.reverse(); return \"/\" + b.join(\"/\") }; KJUR.asn1.x509.RDN = function (a) { KJUR.asn1.x509.RDN.superclass.constructor.call(this); this.asn1Array = new Array(); this.addByString = function (b) { this.asn1Array.push(new KJUR.asn1.x509.AttributeTypeAndValue({ str: b })) }; this.addByMultiValuedString = function (d) { var b = KJUR.asn1.x509.RDN.parseString(d); for (var c = 0; c < b.length; c++) { this.addByString(b[c]) } }; this.getEncodedHex = function () { var b = new KJUR.asn1.DERSet({ array: this.asn1Array }); this.TLV = b.getEncodedHex(); return this.TLV }; if (typeof a != \"undefined\") { if (typeof a.str != \"undefined\") { this.addByMultiValuedString(a.str) } } }; YAHOO.lang.extend(KJUR.asn1.x509.RDN, KJUR.asn1.ASN1Object); KJUR.asn1.x509.RDN.parseString = function (m) { var j = m.split(/\\+/); var h = false; var c = []; for (var g = 0; j.length > 0; g++) { var k = j.shift(); if (h === true) { var f = c.pop(); var d = (f + \"+\" + k).replace(/\\\\\\+/g, \"+\"); c.push(d); h = false } else { c.push(k) } if (k.substr(-1, 1) === \"\\\\\") { h = true } } var l = false; var b = []; for (var g = 0; c.length > 0; g++) { var k = c.shift(); if (l === true) { var e = b.pop(); if (k.match(/\"$/)) { var d = (e + \"+\" + k).replace(/^([^=]+)=\"(.*)\"$/, \"$1=$2\"); b.push(d); l = false } else { b.push(e + \"+\" + k) } } else { b.push(k) } if (k.match(/^[^=]+=\"/)) { l = true } } return b }; KJUR.asn1.x509.AttributeTypeAndValue = function (d) { KJUR.asn1.x509.AttributeTypeAndValue.superclass.constructor.call(this); var f = null, e = null, a = \"utf8\", c = KJUR, b = c.asn1; this.setByString = function (h) { var g = h.match(/^([^=]+)=(.+)$/); if (g) { this.setByAttrTypeAndValueStr(g[1], g[2]) } else { throw \"malformed attrTypeAndValueStr: \" + h } }; this.setByAttrTypeAndValueStr = function (i, h) { this.typeObj = KJUR.asn1.x509.OID.atype2obj(i); var g = a; if (i == \"C\") { g = \"prn\" } this.valueObj = this.getValueObj(g, h) }; this.getValueObj = function (h, g) { if (h == \"utf8\") { return new b.DERUTF8String({ str: g }) } if (h == \"prn\") { return new b.DERPrintableString({ str: g }) } if (h == \"tel\") { return new b.DERTeletexString({ str: g }) } if (h == \"ia5\") { return new b.DERIA5String({ str: g }) } throw \"unsupported directory string type: type=\" + h + \" value=\" + g }; this.getEncodedHex = function () { var g = new b.DERSequence({ array: [this.typeObj, this.valueObj] }); this.TLV = g.getEncodedHex(); return this.TLV }; if (typeof d != \"undefined\") { if (typeof d.str != \"undefined\") { this.setByString(d.str) } } }; YAHOO.lang.extend(KJUR.asn1.x509.AttributeTypeAndValue, KJUR.asn1.ASN1Object); KJUR.asn1.x509.SubjectPublicKeyInfo = function (f) { KJUR.asn1.x509.SubjectPublicKeyInfo.superclass.constructor.call(this); var l = null, k = null, a = KJUR, j = a.asn1, i = j.DERInteger, b = j.DERBitString, m = j.DERObjectIdentifier, e = j.DERSequence, h = j.ASN1Util.newObject, d = j.x509, o = d.AlgorithmIdentifier, g = a.crypto, n = g.ECDSA, c = g.DSA; this.getASN1Object = function () { if (this.asn1AlgId == null || this.asn1SubjPKey == null) { throw \"algId and/or subjPubKey not set\" } var p = new e({ array: [this.asn1AlgId, this.asn1SubjPKey] }); return p }; this.getEncodedHex = function () { var p = this.getASN1Object(); this.hTLV = p.getEncodedHex(); return this.hTLV }; this.setPubKey = function (q) { try { if (q instanceof RSAKey) { var u = h({ seq: [{ \"int\": { bigint: q.n } }, { \"int\": { \"int\": q.e } }] }); var s = u.getEncodedHex(); this.asn1AlgId = new o({ name: \"rsaEncryption\" }); this.asn1SubjPKey = new b({ hex: \"00\" + s }) } } catch (p) { } try { if (q instanceof KJUR.crypto.ECDSA) { var r = new m({ name: q.curveName }); this.asn1AlgId = new o({ name: \"ecPublicKey\", asn1params: r }); this.asn1SubjPKey = new b({ hex: \"00\" + q.pubKeyHex }) } } catch (p) { } try { if (q instanceof KJUR.crypto.DSA) { var r = new h({ seq: [{ \"int\": { bigint: q.p } }, { \"int\": { bigint: q.q } }, { \"int\": { bigint: q.g } }] }); this.asn1AlgId = new o({ name: \"dsa\", asn1params: r }); var t = new i({ bigint: q.y }); this.asn1SubjPKey = new b({ hex: \"00\" + t.getEncodedHex() }) } } catch (p) { } }; if (f !== undefined) { this.setPubKey(f) } }; YAHOO.lang.extend(KJUR.asn1.x509.SubjectPublicKeyInfo, KJUR.asn1.ASN1Object); KJUR.asn1.x509.Time = function (f) { KJUR.asn1.x509.Time.superclass.constructor.call(this); var e = null, a = null, d = KJUR, c = d.asn1, b = c.DERUTCTime, g = c.DERGeneralizedTime; this.setTimeParams = function (h) { this.timeParams = h }; this.getEncodedHex = function () { var h = null; if (this.timeParams != null) { if (this.type == \"utc\") { h = new b(this.timeParams) } else { h = new g(this.timeParams) } } else { if (this.type == \"utc\") { h = new b() } else { h = new g() } } this.TLV = h.getEncodedHex(); return this.TLV }; this.type = \"utc\"; if (f !== undefined) { if (f.type !== undefined) { this.type = f.type } else { if (f.str !== undefined) { if (f.str.match(/^[0-9]{12}Z$/)) { this.type = \"utc\" } if (f.str.match(/^[0-9]{14}Z$/)) { this.type = \"gen\" } } } this.timeParams = f } }; YAHOO.lang.extend(KJUR.asn1.x509.Time, KJUR.asn1.ASN1Object); KJUR.asn1.x509.AlgorithmIdentifier = function (d) { KJUR.asn1.x509.AlgorithmIdentifier.superclass.constructor.call(this); this.nameAlg = null; this.asn1Alg = null; this.asn1Params = null; this.paramEmpty = false; var b = KJUR, a = b.asn1; this.getEncodedHex = function () { if (this.nameAlg === null && this.asn1Alg === null) { throw \"algorithm not specified\" } if (this.nameAlg !== null && this.asn1Alg === null) { this.asn1Alg = a.x509.OID.name2obj(this.nameAlg) } var e = [this.asn1Alg]; if (this.asn1Params !== null) { e.push(this.asn1Params) } var f = new a.DERSequence({ array: e }); this.hTLV = f.getEncodedHex(); return this.hTLV }; if (d !== undefined) { if (d.name !== undefined) { this.nameAlg = d.name } if (d.asn1params !== undefined) { this.asn1Params = d.asn1params } if (d.paramempty !== undefined) { this.paramEmpty = d.paramempty } } if (this.asn1Params === null && this.paramEmpty === false && this.nameAlg !== null) { var c = this.nameAlg.toLowerCase(); if (c.substr(-7, 7) !== \"withdsa\" && c.substr(-9, 9) !== \"withecdsa\") { this.asn1Params = new a.DERNull() } } }; YAHOO.lang.extend(KJUR.asn1.x509.AlgorithmIdentifier, KJUR.asn1.ASN1Object); KJUR.asn1.x509.GeneralName = function (e) { KJUR.asn1.x509.GeneralName.superclass.constructor.call(this); var k = null, h = null, i = { rfc822: \"81\", dns: \"82\", dn: \"a4\", uri: \"86\" }, b = KJUR, f = b.asn1, d = f.DERIA5String, c = f.DERTaggedObject, j = f.ASN1Object, a = f.x509.X500Name, g = pemtohex; this.explicit = false; this.setByParam = function (r) { var q = null; var n = null; if (r === undefined) { return } if (r.rfc822 !== undefined) { this.type = \"rfc822\"; n = new d({ str: r[this.type] }) } if (r.dns !== undefined) { this.type = \"dns\"; n = new d({ str: r[this.type] }) } if (r.uri !== undefined) { this.type = \"uri\"; n = new d({ str: r[this.type] }) } if (r.dn !== undefined) { this.type = \"dn\"; n = new a({ str: r.dn }) } if (r.ldapdn !== undefined) { this.type = \"dn\"; n = new a({ ldapstr: r.ldapdn }) } if (r.certissuer !== undefined) { this.type = \"dn\"; this.explicit = true; var o = r.certissuer; var m = null; if (o.match(/^[0-9A-Fa-f]+$/)) { m == o } if (o.indexOf(\"-----BEGIN \") != -1) { m = g(o) } if (m == null) { throw \"certissuer param not cert\" } var l = new X509(); l.hex = m; var p = l.getIssuerHex(); n = new j(); n.hTLV = p } if (r.certsubj !== undefined) { this.type = \"dn\"; this.explicit = true; var o = r.certsubj; var m = null; if (o.match(/^[0-9A-Fa-f]+$/)) { m == o } if (o.indexOf(\"-----BEGIN \") != -1) { m = g(o) } if (m == null) { throw \"certsubj param not cert\" } var l = new X509(); l.hex = m; var p = l.getSubjectHex(); n = new j(); n.hTLV = p } if (this.type == null) { throw \"unsupported type in params=\" + r } this.asn1Obj = new c({ explicit: this.explicit, tag: i[this.type], obj: n }) }; this.getEncodedHex = function () { return this.asn1Obj.getEncodedHex() }; if (e !== undefined) { this.setByParam(e) } }; YAHOO.lang.extend(KJUR.asn1.x509.GeneralName, KJUR.asn1.ASN1Object); KJUR.asn1.x509.GeneralNames = function (d) { KJUR.asn1.x509.GeneralNames.superclass.constructor.call(this); var a = null, c = KJUR, b = c.asn1; this.setByParamArray = function (g) { for (var e = 0; e < g.length; e++) { var f = new b.x509.GeneralName(g[e]); this.asn1Array.push(f) } }; this.getEncodedHex = function () { var e = new b.DERSequence({ array: this.asn1Array }); return e.getEncodedHex() }; this.asn1Array = new Array(); if (typeof d != \"undefined\") { this.setByParamArray(d) } }; YAHOO.lang.extend(KJUR.asn1.x509.GeneralNames, KJUR.asn1.ASN1Object); KJUR.asn1.x509.DistributionPointName = function (b) { KJUR.asn1.x509.DistributionPointName.superclass.constructor.call(this); var h = null, e = null, a = null, g = null, d = KJUR, c = d.asn1, f = c.DERTaggedObject; this.getEncodedHex = function () { if (this.type != \"full\") { throw \"currently type shall be 'full': \" + this.type } this.asn1Obj = new f({ explicit: false, tag: this.tag, obj: this.asn1V }); this.hTLV = this.asn1Obj.getEncodedHex(); return this.hTLV }; if (b !== undefined) { if (c.x509.GeneralNames.prototype.isPrototypeOf(b)) { this.type = \"full\"; this.tag = \"a0\"; this.asn1V = b } else { throw \"This class supports GeneralNames only as argument\" } } }; YAHOO.lang.extend(KJUR.asn1.x509.DistributionPointName, KJUR.asn1.ASN1Object); KJUR.asn1.x509.DistributionPoint = function (d) { KJUR.asn1.x509.DistributionPoint.superclass.constructor.call(this); var a = null, c = KJUR, b = c.asn1; this.getEncodedHex = function () { var e = new b.DERSequence(); if (this.asn1DP != null) { var f = new b.DERTaggedObject({ explicit: true, tag: \"a0\", obj: this.asn1DP }); e.appendASN1Object(f) } this.hTLV = e.getEncodedHex(); return this.hTLV }; if (d !== undefined) { if (d.dpobj !== undefined) { this.asn1DP = d.dpobj } } }; YAHOO.lang.extend(KJUR.asn1.x509.DistributionPoint, KJUR.asn1.ASN1Object); KJUR.asn1.x509.OID = new function (a) { this.atype2oidList = { CN: \"2.5.4.3\", L: \"2.5.4.7\", ST: \"2.5.4.8\", O: \"2.5.4.10\", OU: \"2.5.4.11\", C: \"2.5.4.6\", STREET: \"2.5.4.9\", DC: \"0.9.2342.19200300.100.1.25\", UID: \"0.9.2342.19200300.100.1.1\", SN: \"2.5.4.4\", DN: \"2.5.4.49\", E: \"1.2.840.113549.1.9.1\", businessCategory: \"2.5.4.15\", postalCode: \"2.5.4.17\", serialNumber: \"2.5.4.5\", jurisdictionOfIncorporationL: \"1.3.6.1.4.1.311.60.2.1.1\", jurisdictionOfIncorporationSP: \"1.3.6.1.4.1.311.60.2.1.2\", jurisdictionOfIncorporationC: \"1.3.6.1.4.1.311.60.2.1.3\" }; this.name2oidList = { sha1: \"1.3.14.3.2.26\", sha256: \"2.16.840.1.101.3.4.2.1\", sha384: \"2.16.840.1.101.3.4.2.2\", sha512: \"2.16.840.1.101.3.4.2.3\", sha224: \"2.16.840.1.101.3.4.2.4\", md5: \"1.2.840.113549.2.5\", md2: \"1.3.14.7.2.2.1\", ripemd160: \"1.3.36.3.2.1\", MD2withRSA: \"1.2.840.113549.1.1.2\", MD4withRSA: \"1.2.840.113549.1.1.3\", MD5withRSA: \"1.2.840.113549.1.1.4\", SHA1withRSA: \"1.2.840.113549.1.1.5\", SHA224withRSA: \"1.2.840.113549.1.1.14\", SHA256withRSA: \"1.2.840.113549.1.1.11\", SHA384withRSA: \"1.2.840.113549.1.1.12\", SHA512withRSA: \"1.2.840.113549.1.1.13\", SHA1withECDSA: \"1.2.840.10045.4.1\", SHA224withECDSA: \"1.2.840.10045.4.3.1\", SHA256withECDSA: \"1.2.840.10045.4.3.2\", SHA384withECDSA: \"1.2.840.10045.4.3.3\", SHA512withECDSA: \"1.2.840.10045.4.3.4\", dsa: \"1.2.840.10040.4.1\", SHA1withDSA: \"1.2.840.10040.4.3\", SHA224withDSA: \"2.16.840.1.101.3.4.3.1\", SHA256withDSA: \"2.16.840.1.101.3.4.3.2\", rsaEncryption: \"1.2.840.113549.1.1.1\", commonName: \"2.5.4.3\", localityName: \"2.5.4.7\", stateOrProvinceName: \"2.5.4.8\", organizationName: \"2.5.4.10\", organizationalUnitName: \"2.5.4.11\", countryName: \"2.5.4.6\", streetAddress: \"2.5.4.9\", domainComponent: \"0.9.2342.19200300.100.1.25\", userId: \"0.9.2342.19200300.100.1.1\", surname: \"2.5.4.4\", distinguishedName: \"2.5.4.49\", emailAddress: \"1.2.840.113549.1.9.1\", businessCategory: \"2.5.4.15\", postalCode: \"2.5.4.17\", jurisdictionOfIncorporationL: \"1.3.6.1.4.1.311.60.2.1.1\", jurisdictionOfIncorporationSP: \"1.3.6.1.4.1.311.60.2.1.2\", jurisdictionOfIncorporationC: \"1.3.6.1.4.1.311.60.2.1.3\", subjectKeyIdentifier: \"2.5.29.14\", keyUsage: \"2.5.29.15\", subjectAltName: \"2.5.29.17\", issuerAltName: \"2.5.29.18\", basicConstraints: \"2.5.29.19\", nameConstraints: \"2.5.29.30\", cRLDistributionPoints: \"2.5.29.31\", certificatePolicies: \"2.5.29.32\", authorityKeyIdentifier: \"2.5.29.35\", policyConstraints: \"2.5.29.36\", extKeyUsage: \"2.5.29.37\", authorityInfoAccess: \"1.3.6.1.5.5.7.1.1\", ocsp: \"1.3.6.1.5.5.7.48.1\", caIssuers: \"1.3.6.1.5.5.7.48.2\", anyExtendedKeyUsage: \"2.5.29.37.0\", serverAuth: \"1.3.6.1.5.5.7.3.1\", clientAuth: \"1.3.6.1.5.5.7.3.2\", codeSigning: \"1.3.6.1.5.5.7.3.3\", emailProtection: \"1.3.6.1.5.5.7.3.4\", timeStamping: \"1.3.6.1.5.5.7.3.8\", ocspSigning: \"1.3.6.1.5.5.7.3.9\", ecPublicKey: \"1.2.840.10045.2.1\", secp256r1: \"1.2.840.10045.3.1.7\", secp256k1: \"1.3.132.0.10\", secp384r1: \"1.3.132.0.34\", pkcs5PBES2: \"1.2.840.113549.1.5.13\", pkcs5PBKDF2: \"1.2.840.113549.1.5.12\", \"des-EDE3-CBC\": \"1.2.840.113549.3.7\", data: \"1.2.840.113549.1.7.1\", \"signed-data\": \"1.2.840.113549.1.7.2\", \"enveloped-data\": \"1.2.840.113549.1.7.3\", \"digested-data\": \"1.2.840.113549.1.7.5\", \"encrypted-data\": \"1.2.840.113549.1.7.6\", \"authenticated-data\": \"1.2.840.113549.1.9.16.1.2\", tstinfo: \"1.2.840.113549.1.9.16.1.4\", extensionRequest: \"1.2.840.113549.1.9.14\", }; this.objCache = {}; this.name2obj = function (b) { if (typeof this.objCache[b] != \"undefined\") { return this.objCache[b] } if (typeof this.name2oidList[b] == \"undefined\") { throw \"Name of ObjectIdentifier not defined: \" + b } var c = this.name2oidList[b]; var d = new KJUR.asn1.DERObjectIdentifier({ oid: c }); this.objCache[b] = d; return d }; this.atype2obj = function (b) { if (typeof this.objCache[b] != \"undefined\") { return this.objCache[b] } if (typeof this.atype2oidList[b] == \"undefined\") { throw \"AttributeType name undefined: \" + b } var c = this.atype2oidList[b]; var d = new KJUR.asn1.DERObjectIdentifier({ oid: c }); this.objCache[b] = d; return d } }; KJUR.asn1.x509.OID.oid2name = function (b) { var c = KJUR.asn1.x509.OID.name2oidList; for (var a in c) { if (c[a] == b) { return a } } return \"\" }; KJUR.asn1.x509.OID.oid2atype = function (b) { var c = KJUR.asn1.x509.OID.atype2oidList; for (var a in c) { if (c[a] == b) { return a } } return b }; KJUR.asn1.x509.OID.name2oid = function (a) { var b = KJUR.asn1.x509.OID.name2oidList; if (b[a] === undefined) { return \"\" } return b[a] }; KJUR.asn1.x509.X509Util = {}; KJUR.asn1.x509.X509Util.newCertPEM = function (h) { var g = KJUR.asn1.x509, b = g.TBSCertificate, a = g.Certificate; var f = new b(); if (h.serial !== undefined) { f.setSerialNumberByParam(h.serial) } else { throw \"serial number undefined.\" } if (typeof h.sigalg.name === \"string\") { f.setSignatureAlgByParam(h.sigalg) } else { throw \"unproper signature algorithm name\" } if (h.issuer !== undefined) { f.setIssuerByParam(h.issuer) } else { throw \"issuer name undefined.\" } if (h.notbefore !== undefined) { f.setNotBeforeByParam(h.notbefore) } else { throw \"notbefore undefined.\" } if (h.notafter !== undefined) { f.setNotAfterByParam(h.notafter) } else { throw \"notafter undefined.\" } if (h.subject !== undefined) { f.setSubjectByParam(h.subject) } else { throw \"subject name undefined.\" } if (h.sbjpubkey !== undefined) { f.setSubjectPublicKeyByGetKey(h.sbjpubkey) } else { throw \"subject public key undefined.\" } if (h.ext !== undefined && h.ext.length !== undefined) { for (var d = 0; d < h.ext.length; d++) { for (key in h.ext[d]) { f.appendExtensionByName(key, h.ext[d][key]) } } } if (h.cakey === undefined && h.sighex === undefined) { throw \"param cakey and sighex undefined.\" } var e = null; var c = null; if (h.cakey) { if (h.cakey.isPrivate === true) { e = h.cakey } else { e = KEYUTIL.getKey.apply(null, h.cakey) } c = new a({ tbscertobj: f, prvkeyobj: e }); c.sign() } if (h.sighex) { c = new a({ tbscertobj: f }); c.setSignatureHex(h.sighex) } return c.getPEMString() };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1) { KJUR.asn1 = {} } if (typeof KJUR.asn1.cms == \"undefined\" || !KJUR.asn1.cms) { KJUR.asn1.cms = {} } KJUR.asn1.cms.Attribute = function (d) { var a = [], c = KJUR, b = c.asn1; b.cms.Attribute.superclass.constructor.call(this); this.getEncodedHex = function () { var h, g, e; h = new b.DERObjectIdentifier({ oid: this.attrTypeOid }); g = new b.DERSet({ array: this.valueList }); try { g.getEncodedHex() } catch (f) { throw \"fail valueSet.getEncodedHex in Attribute(1)/\" + f } e = new b.DERSequence({ array: [h, g] }); try { this.hTLV = e.getEncodedHex() } catch (f) { throw \"failed seq.getEncodedHex in Attribute(2)/\" + f } return this.hTLV } }; YAHOO.lang.extend(KJUR.asn1.cms.Attribute, KJUR.asn1.ASN1Object); KJUR.asn1.cms.ContentType = function (d) { var c = KJUR, b = c.asn1; b.cms.ContentType.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.3\"; var a = null; if (typeof d != \"undefined\") { var a = new b.DERObjectIdentifier(d); this.valueList = [a] } }; YAHOO.lang.extend(KJUR.asn1.cms.ContentType, KJUR.asn1.cms.Attribute); KJUR.asn1.cms.MessageDigest = function (d) { var b = KJUR, e = b.asn1, g = e.DEROctetString, i = e.cms; i.MessageDigest.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.4\"; if (d !== undefined) { if (d.eciObj instanceof i.EncapsulatedContentInfo && typeof d.hashAlg === \"string\") { var h = d.eciObj.eContentValueHex; var c = d.hashAlg; var a = b.crypto.Util.hashHex(h, c); var f = new g({ hex: a }); f.getEncodedHex(); this.valueList = [f] } else { var f = new g(d); f.getEncodedHex(); this.valueList = [f] } } }; YAHOO.lang.extend(KJUR.asn1.cms.MessageDigest, KJUR.asn1.cms.Attribute); KJUR.asn1.cms.SigningTime = function (e) { var d = KJUR, c = d.asn1; c.cms.SigningTime.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.5\"; if (e !== undefined) { var a = new c.x509.Time(e); try { a.getEncodedHex() } catch (b) { throw \"SigningTime.getEncodedHex() failed/\" + b } this.valueList = [a] } }; YAHOO.lang.extend(KJUR.asn1.cms.SigningTime, KJUR.asn1.cms.Attribute); KJUR.asn1.cms.SigningCertificate = function (f) { var c = KJUR, b = c.asn1, a = b.DERSequence, e = b.cms, d = c.crypto; e.SigningCertificate.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.16.2.12\"; this.setCerts = function (n) { var l = []; for (var k = 0; k < n.length; k++) { var h = pemtohex(n[k]); var g = c.crypto.Util.hashHex(h, \"sha1\"); var o = new b.DEROctetString({ hex: g }); o.getEncodedHex(); var m = new e.IssuerAndSerialNumber({ cert: n[k] }); m.getEncodedHex(); var p = new a({ array: [o, m] }); p.getEncodedHex(); l.push(p) } var j = new a({ array: l }); j.getEncodedHex(); this.valueList = [j] }; if (f !== undefined) { if (typeof f.array == \"object\") { this.setCerts(f.array) } } }; YAHOO.lang.extend(KJUR.asn1.cms.SigningCertificate, KJUR.asn1.cms.Attribute); KJUR.asn1.cms.SigningCertificateV2 = function (h) { var d = KJUR, c = d.asn1, b = c.DERSequence, g = c.x509, f = c.cms, e = d.crypto; f.SigningCertificateV2.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.16.2.47\"; this.setCerts = function (r, k) { var p = []; for (var n = 0; n < r.length; n++) { var l = pemtohex(r[n]); var t = []; if (k !== \"sha256\") { t.push(new g.AlgorithmIdentifier({ name: k })) } var j = e.Util.hashHex(l, k); var s = new c.DEROctetString({ hex: j }); s.getEncodedHex(); t.push(s); var o = new f.IssuerAndSerialNumber({ cert: r[n] }); o.getEncodedHex(); t.push(o); var q = new b({ array: t }); q.getEncodedHex(); p.push(q) } var m = new b({ array: p }); m.getEncodedHex(); this.valueList = [m] }; if (h !== undefined) { if (typeof h.array == \"object\") { var a = \"sha256\"; if (typeof h.hashAlg == \"string\") { a = h.hashAlg } this.setCerts(h.array, a) } } }; YAHOO.lang.extend(KJUR.asn1.cms.SigningCertificateV2, KJUR.asn1.cms.Attribute); KJUR.asn1.cms.IssuerAndSerialNumber = function (e) { var b = KJUR, g = b.asn1, f = g.DERInteger, i = g.cms, d = g.x509, a = d.X500Name, c = X509; i.IssuerAndSerialNumber.superclass.constructor.call(this); var j = null; var h = null; this.setByCertPEM = function (n) { var l = pemtohex(n); var k = new c(); k.hex = l; var o = k.getIssuerHex(); this.dIssuer = new a(); this.dIssuer.hTLV = o; var m = k.getSerialNumberHex(); this.dSerial = new f({ hex: m }) }; this.getEncodedHex = function () { var k = new g.DERSequence({ array: [this.dIssuer, this.dSerial] }); this.hTLV = k.getEncodedHex(); return this.hTLV }; if (e !== undefined) { if (typeof e == \"string\" && e.indexOf(\"-----BEGIN \") != -1) { this.setByCertPEM(e) } if (e.issuer && e.serial) { if (e.issuer instanceof a) { this.dIssuer = e.issuer } else { this.dIssuer = new a(e.issuer) } if (e.serial instanceof f) { this.dSerial = e.serial } else { this.dSerial = new f(e.serial) } } if (typeof e.cert == \"string\") { this.setByCertPEM(e.cert) } } }; YAHOO.lang.extend(KJUR.asn1.cms.IssuerAndSerialNumber, KJUR.asn1.ASN1Object); KJUR.asn1.cms.AttributeList = function (d) { var b = KJUR, a = b.asn1, c = a.cms; c.AttributeList.superclass.constructor.call(this); this.list = new Array(); this.sortFlag = true; this.add = function (e) { if (e instanceof c.Attribute) { this.list.push(e) } }; this.length = function () { return this.list.length }; this.clear = function () { this.list = new Array(); this.hTLV = null; this.hV = null }; this.getEncodedHex = function () { if (typeof this.hTLV == \"string\") { return this.hTLV } var e = new a.DERSet({ array: this.list, sortflag: this.sortFlag }); this.hTLV = e.getEncodedHex(); return this.hTLV }; if (d !== undefined) { if (typeof d.sortflag != \"undefined\" && d.sortflag == false) { this.sortFlag = false } } }; YAHOO.lang.extend(KJUR.asn1.cms.AttributeList, KJUR.asn1.ASN1Object); KJUR.asn1.cms.SignerInfo = function (e) { var a = KJUR, h = a.asn1, b = h.DERTaggedObject, n = h.cms, j = n.AttributeList, g = n.ContentType, k = n.EncapsulatedContentInfo, c = n.MessageDigest, l = n.SignedData, d = h.x509, m = d.AlgorithmIdentifier, f = a.crypto, i = KEYUTIL; n.SignerInfo.superclass.constructor.call(this); this.dCMSVersion = new h.DERInteger({ \"int\": 1 }); this.dSignerIdentifier = null; this.dDigestAlgorithm = null; this.dSignedAttrs = new j(); this.dSigAlg = null; this.dSig = null; this.dUnsignedAttrs = new j(); this.setSignerIdentifier = function (p) { if (typeof p == \"string\" && p.indexOf(\"CERTIFICATE\") != -1 && p.indexOf(\"BEGIN\") != -1 && p.indexOf(\"END\") != -1) { var o = p; this.dSignerIdentifier = new n.IssuerAndSerialNumber({ cert: p }) } }; this.setForContentAndHash = function (o) { if (o !== undefined) { if (o.eciObj instanceof k) { this.dSignedAttrs.add(new g({ oid: \"1.2.840.113549.1.7.1\" })); this.dSignedAttrs.add(new c({ eciObj: o.eciObj, hashAlg: o.hashAlg })) } if (o.sdObj !== undefined && o.sdObj instanceof l) { if (o.sdObj.digestAlgNameList.join(\":\").indexOf(o.hashAlg) == -1) { o.sdObj.digestAlgNameList.push(o.hashAlg) } } if (typeof o.hashAlg == \"string\") { this.dDigestAlgorithm = new m({ name: o.hashAlg }) } } }; this.sign = function (t, p) { this.dSigAlg = new m({ name: p }); var q = this.dSignedAttrs.getEncodedHex(); var o = i.getKey(t); var s = new f.Signature({ alg: p }); s.init(o); s.updateHex(q); var r = s.sign(); this.dSig = new h.DEROctetString({ hex: r }) }; this.addUnsigned = function (o) { this.hTLV = null; this.dUnsignedAttrs.hTLV = null; this.dUnsignedAttrs.add(o) }; this.getEncodedHex = function () { if (this.dSignedAttrs instanceof j && this.dSignedAttrs.length() == 0) { throw \"SignedAttrs length = 0 (empty)\" } var o = new b({ obj: this.dSignedAttrs, tag: \"a0\", explicit: false }); var r = null; if (this.dUnsignedAttrs.length() > 0) { r = new b({ obj: this.dUnsignedAttrs, tag: \"a1\", explicit: false }) } var q = [this.dCMSVersion, this.dSignerIdentifier, this.dDigestAlgorithm, o, this.dSigAlg, this.dSig,]; if (r != null) { q.push(r) } var p = new h.DERSequence({ array: q }); this.hTLV = p.getEncodedHex(); return this.hTLV } }; YAHOO.lang.extend(KJUR.asn1.cms.SignerInfo, KJUR.asn1.ASN1Object); KJUR.asn1.cms.EncapsulatedContentInfo = function (g) { var c = KJUR, b = c.asn1, e = b.DERTaggedObject, a = b.DERSequence, h = b.DERObjectIdentifier, d = b.DEROctetString, f = b.cms; f.EncapsulatedContentInfo.superclass.constructor.call(this); this.dEContentType = new h({ name: \"data\" }); this.dEContent = null; this.isDetached = false; this.eContentValueHex = null; this.setContentType = function (i) { if (i.match(/^[0-2][.][0-9.]+$/)) { this.dEContentType = new h({ oid: i }) } else { this.dEContentType = new h({ name: i }) } }; this.setContentValue = function (i) { if (i !== undefined) { if (typeof i.hex == \"string\") { this.eContentValueHex = i.hex } else { if (typeof i.str == \"string\") { this.eContentValueHex = utf8tohex(i.str) } } } }; this.setContentValueHex = function (i) { this.eContentValueHex = i }; this.setContentValueStr = function (i) { this.eContentValueHex = utf8tohex(i) }; this.getEncodedHex = function () { if (typeof this.eContentValueHex != \"string\") { throw \"eContentValue not yet set\" } var k = new d({ hex: this.eContentValueHex }); this.dEContent = new e({ obj: k, tag: \"a0\", explicit: true }); var i = [this.dEContentType]; if (!this.isDetached) { i.push(this.dEContent) } var j = new a({ array: i }); this.hTLV = j.getEncodedHex(); return this.hTLV } }; YAHOO.lang.extend(KJUR.asn1.cms.EncapsulatedContentInfo, KJUR.asn1.ASN1Object); KJUR.asn1.cms.ContentInfo = function (f) { var c = KJUR, b = c.asn1, d = b.DERTaggedObject, a = b.DERSequence, e = b.x509; KJUR.asn1.cms.ContentInfo.superclass.constructor.call(this); this.dContentType = null; this.dContent = null; this.setContentType = function (g) { if (typeof g == \"string\") { this.dContentType = e.OID.name2obj(g) } }; this.getEncodedHex = function () { var h = new d({ obj: this.dContent, tag: \"a0\", explicit: true }); var g = new a({ array: [this.dContentType, h] }); this.hTLV = g.getEncodedHex(); return this.hTLV }; if (f !== undefined) { if (f.type) { this.setContentType(f.type) } if (f.obj && f.obj instanceof b.ASN1Object) { this.dContent = f.obj } } }; YAHOO.lang.extend(KJUR.asn1.cms.ContentInfo, KJUR.asn1.ASN1Object); KJUR.asn1.cms.SignedData = function (e) { var a = KJUR, h = a.asn1, j = h.ASN1Object, g = h.DERInteger, m = h.DERSet, f = h.DERSequence, b = h.DERTaggedObject, l = h.cms, i = l.EncapsulatedContentInfo, d = l.SignerInfo, n = l.ContentInfo, c = h.x509, k = c.AlgorithmIdentifier; KJUR.asn1.cms.SignedData.superclass.constructor.call(this); this.dCMSVersion = new g({ \"int\": 1 }); this.dDigestAlgs = null; this.digestAlgNameList = []; this.dEncapContentInfo = new i(); this.dCerts = null; this.certificateList = []; this.crlList = []; this.signerInfoList = [new d()]; this.addCertificatesByPEM = function (p) { var q = pemtohex(p); var r = new j(); r.hTLV = q; this.certificateList.push(r) }; this.getEncodedHex = function () { if (typeof this.hTLV == \"string\") { return this.hTLV } if (this.dDigestAlgs == null) { var u = []; for (var t = 0; t < this.digestAlgNameList.length; t++) { var s = this.digestAlgNameList[t]; var w = new k({ name: s }); u.push(w) } this.dDigestAlgs = new m({ array: u }) } var p = [this.dCMSVersion, this.dDigestAlgs, this.dEncapContentInfo]; if (this.dCerts == null) { if (this.certificateList.length > 0) { var v = new m({ array: this.certificateList }); this.dCerts = new b({ obj: v, tag: \"a0\", explicit: false }) } } if (this.dCerts != null) { p.push(this.dCerts) } var r = new m({ array: this.signerInfoList }); p.push(r); var q = new f({ array: p }); this.hTLV = q.getEncodedHex(); return this.hTLV }; this.getContentInfo = function () { this.getEncodedHex(); var o = new n({ type: \"signed-data\", obj: this }); return o }; this.getContentInfoEncodedHex = function () { var o = this.getContentInfo(); var p = o.getEncodedHex(); return p }; this.getPEM = function () { return hextopem(this.getContentInfoEncodedHex(), \"CMS\") } }; YAHOO.lang.extend(KJUR.asn1.cms.SignedData, KJUR.asn1.ASN1Object); KJUR.asn1.cms.CMSUtil = new function () { }; KJUR.asn1.cms.CMSUtil.newSignedData = function (d) { var b = KJUR, j = b.asn1, q = j.cms, f = q.SignerInfo, n = q.SignedData, o = q.SigningTime, a = q.SigningCertificate, p = q.SigningCertificateV2, c = j.cades, e = c.SignaturePolicyIdentifier; var m = new n(); m.dEncapContentInfo.setContentValue(d.content); if (typeof d.certs == \"object\") { for (var h = 0; h < d.certs.length; h++) { m.addCertificatesByPEM(d.certs[h]) } } m.signerInfoList = []; for (var h = 0; h < d.signerInfos.length; h++) { var k = d.signerInfos[h]; var g = new f(); g.setSignerIdentifier(k.signerCert); g.setForContentAndHash({ sdObj: m, eciObj: m.dEncapContentInfo, hashAlg: k.hashAlg }); for (attrName in k.sAttr) { var r = k.sAttr[attrName]; if (attrName == \"SigningTime\") { var l = new o(r); g.dSignedAttrs.add(l) } if (attrName == \"SigningCertificate\") { var l = new a(r); g.dSignedAttrs.add(l) } if (attrName == \"SigningCertificateV2\") { var l = new p(r); g.dSignedAttrs.add(l) } if (attrName == \"SignaturePolicyIdentifier\") { var l = new e(r); g.dSignedAttrs.add(l) } } g.sign(k.signerPrvKey, k.sigAlg); m.signerInfoList.push(g) } return m }; KJUR.asn1.cms.CMSUtil.verifySignedData = function (n) { var C = KJUR, p = C.asn1, s = p.cms, D = s.SignerInfo, q = s.SignedData, y = s.SigningTime, b = s.SigningCertificate, d = s.SigningCertificateV2, A = p.cades, u = A.SignaturePolicyIdentifier, i = C.lang.String.isHex, v = ASN1HEX, h = v.getVbyList, a = v.getTLVbyList, t = v.getIdxbyList, z = v.getChildIdx, c = v.getTLV, B = v.oidname, j = C.crypto.Util.hashHex; if (n.cms === undefined && !i(n.cms)) { } var E = n.cms; var g = function (J, H) { var G; for (var I = 3; I < 6; I++) { G = t(J, 0, [1, 0, I]); if (G !== undefined) { var F = J.substr(G, 2); if (F === \"a0\") { H.certsIdx = G } if (F === \"a1\") { H.revinfosIdx = G } if (F === \"31\") { H.signerinfosIdx = G } } } }; var l = function (I, F) { var H = F.signerinfosIdx; if (H === undefined) { return } var L = z(I, H); F.signerInfoIdxList = L; for (var G = 0; G < L.length; G++) { var K = L[G]; var J = { idx: K }; k(I, J); F.signerInfos.push(J) } }; var k = function (I, J) { var F = J.idx; J.signerid_issuer1 = a(I, F, [1, 0], \"30\"); J.signerid_serial1 = h(I, F, [1, 1], \"02\"); J.hashalg = B(h(I, F, [2, 0], \"06\")); var H = t(I, F, [3], \"a0\"); J.idxSignedAttrs = H; f(I, J, H); var G = z(I, F); var K = G.length; if (K < 6) { throw \"malformed SignerInfo\" } J.sigalg = B(h(I, F, [K - 2, 0], \"06\")); J.sigval = h(I, F, [K - 1], \"04\") }; var f = function (L, M, F) { var J = z(L, F); M.signedAttrIdxList = J; for (var K = 0; K < J.length; K++) { var I = J[K]; var G = h(L, I, [0], \"06\"); var H; if (G === \"2a864886f70d010905\") { H = hextoutf8(h(L, I, [1, 0])); M.saSigningTime = H } else { if (G === \"2a864886f70d010904\") { H = h(L, I, [1, 0], \"04\"); M.saMessageDigest = H } } } }; var w = function (G, F) { if (h(G, 0, [0], \"06\") !== \"2a864886f70d010702\") { return F } F.cmsType = \"signedData\"; F.econtent = h(G, 0, [1, 0, 2, 1, 0]); g(G, F); F.signerInfos = []; l(G, F) }; var o = function (J, F) { var G = F.parse.signerInfos; var L = G.length; var K = true; for (var I = 0; I < L; I++) { var H = G[I]; e(J, F, H, I); if (!H.isValid) { K = false } } F.isValid = K }; var x = function (F, Q, J, P) { var N = Q.parse.certsIdx; var H; if (Q.certs === undefined) { H = []; Q.certkeys = []; var K = z(F, N); for (var I = 0; I < K.length; I++) { var M = c(F, K[I]); var O = new X509(); O.readCertHex(M); H[I] = O; Q.certkeys[I] = O.getPublicKey() } Q.certs = H } else { H = Q.certs } Q.cccc = H.length; Q.cccci = K.length; for (var I = 0; I < H.length; I++) { var L = O.getIssuerHex(); var G = O.getSerialNumberHex(); if (J.signerid_issuer1 === L && J.signerid_serial1 === G) { J.certkey_idx = I } } }; var e = function (F, R, I, N) { I.verifyDetail = {}; var Q = I.verifyDetail; var K = R.parse.econtent; var G = I.hashalg; var L = I.saMessageDigest; Q.validMessageDigest = false; if (j(K, G) === L) { Q.validMessageDigest = true } x(F, R, I, N); Q.validSignatureValue = false; var H = I.sigalg; var M = \"31\" + c(F, I.idxSignedAttrs).substr(2); I.signedattrshex = M; var J = R.certs[I.certkey_idx].getPublicKey(); var P = new KJUR.crypto.Signature({ alg: H }); P.init(J); P.updateHex(M); var O = P.verify(I.sigval); Q.validSignatureValue_isValid = O; if (O === true) { Q.validSignatureValue = true } I.isValid = false; if (Q.validMessageDigest && Q.validSignatureValue) { I.isValid = true } }; var m = function () { }; var r = { isValid: false, parse: {} }; w(E, r.parse); o(E, r); return r };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1) { KJUR.asn1 = {} } if (typeof KJUR.asn1.tsp == \"undefined\" || !KJUR.asn1.tsp) { KJUR.asn1.tsp = {} } KJUR.asn1.tsp.Accuracy = function (f) { var c = KJUR, b = c.asn1, e = b.DERInteger, a = b.DERSequence, d = b.DERTaggedObject; b.tsp.Accuracy.superclass.constructor.call(this); this.seconds = null; this.millis = null; this.micros = null; this.getEncodedHex = function () { var i = null; var k = null; var m = null; var g = []; if (this.seconds != null) { i = new e({ \"int\": this.seconds }); g.push(i) } if (this.millis != null) { var l = new e({ \"int\": this.millis }); k = new d({ obj: l, tag: \"80\", explicit: false }); g.push(k) } if (this.micros != null) { var j = new e({ \"int\": this.micros }); m = new d({ obj: j, tag: \"81\", explicit: false }); g.push(m) } var h = new a({ array: g }); this.hTLV = h.getEncodedHex(); return this.hTLV }; if (f !== undefined) { if (typeof f.seconds == \"number\") { this.seconds = f.seconds } if (typeof f.millis == \"number\") { this.millis = f.millis } if (typeof f.micros == \"number\") { this.micros = f.micros } } }; YAHOO.lang.extend(KJUR.asn1.tsp.Accuracy, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.MessageImprint = function (g) { var c = KJUR, b = c.asn1, a = b.DERSequence, d = b.DEROctetString, f = b.x509, e = f.AlgorithmIdentifier; b.tsp.MessageImprint.superclass.constructor.call(this); this.dHashAlg = null; this.dHashValue = null; this.getEncodedHex = function () { if (typeof this.hTLV == \"string\") { return this.hTLV } var h = new a({ array: [this.dHashAlg, this.dHashValue] }); return h.getEncodedHex() }; if (g !== undefined) { if (typeof g.hashAlg == \"string\") { this.dHashAlg = new e({ name: g.hashAlg }) } if (typeof g.hashValue == \"string\") { this.dHashValue = new d({ hex: g.hashValue }) } } }; YAHOO.lang.extend(KJUR.asn1.tsp.MessageImprint, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.TimeStampReq = function (c) { var a = KJUR, f = a.asn1, d = f.DERSequence, e = f.DERInteger, g = f.DERBoolean, i = f.DERObjectIdentifier, h = f.tsp, b = h.MessageImprint; h.TimeStampReq.superclass.constructor.call(this); this.dVersion = new e({ \"int\": 1 }); this.dMessageImprint = null; this.dPolicy = null; this.dNonce = null; this.certReq = true; this.setMessageImprint = function (j) { if (j instanceof b) { this.dMessageImprint = j; return } if (typeof j == \"object\") { this.dMessageImprint = new b(j) } }; this.getEncodedHex = function () { if (this.dMessageImprint == null) { throw \"messageImprint shall be specified\" } var j = [this.dVersion, this.dMessageImprint]; if (this.dPolicy != null) { j.push(this.dPolicy) } if (this.dNonce != null) { j.push(this.dNonce) } if (this.certReq) { j.push(new g()) } var k = new d({ array: j }); this.hTLV = k.getEncodedHex(); return this.hTLV }; if (c !== undefined) { if (typeof c.mi == \"object\") { this.setMessageImprint(c.mi) } if (typeof c.policy == \"object\") { this.dPolicy = new i(c.policy) } if (typeof c.nonce == \"object\") { this.dNonce = new e(c.nonce) } if (typeof c.certreq == \"boolean\") { this.certReq = c.certreq } } }; YAHOO.lang.extend(KJUR.asn1.tsp.TimeStampReq, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.TSTInfo = function (e) { var c = KJUR, i = c.asn1, f = i.DERSequence, h = i.DERInteger, k = i.DERBoolean, g = i.DERGeneralizedTime, l = i.DERObjectIdentifier, j = i.tsp, d = j.MessageImprint, b = j.Accuracy, a = i.x509.X500Name; j.TSTInfo.superclass.constructor.call(this); this.dVersion = new h({ \"int\": 1 }); this.dPolicy = null; this.dMessageImprint = null; this.dSerialNumber = null; this.dGenTime = null; this.dAccuracy = null; this.dOrdering = null; this.dNonce = null; this.dTsa = null; this.getEncodedHex = function () { var m = [this.dVersion]; if (this.dPolicy == null) { throw \"policy shall be specified.\" } m.push(this.dPolicy); if (this.dMessageImprint == null) { throw \"messageImprint shall be specified.\" } m.push(this.dMessageImprint); if (this.dSerialNumber == null) { throw \"serialNumber shall be specified.\" } m.push(this.dSerialNumber); if (this.dGenTime == null) { throw \"genTime shall be specified.\" } m.push(this.dGenTime); if (this.dAccuracy != null) { m.push(this.dAccuracy) } if (this.dOrdering != null) { m.push(this.dOrdering) } if (this.dNonce != null) { m.push(this.dNonce) } if (this.dTsa != null) { m.push(this.dTsa) } var n = new f({ array: m }); this.hTLV = n.getEncodedHex(); return this.hTLV }; if (e !== undefined) { if (typeof e.policy == \"string\") { if (!e.policy.match(/^[0-9.]+$/)) { throw \"policy shall be oid like 0.1.4.134\" } this.dPolicy = new l({ oid: e.policy }) } if (e.messageImprint !== undefined) { this.dMessageImprint = new d(e.messageImprint) } if (e.serialNumber !== undefined) { this.dSerialNumber = new h(e.serialNumber) } if (e.genTime !== undefined) { this.dGenTime = new g(e.genTime) } if (e.accuracy !== undefined) { this.dAccuracy = new b(e.accuracy) } if (e.ordering !== undefined && e.ordering == true) { this.dOrdering = new k() } if (e.nonce !== undefined) { this.dNonce = new h(e.nonce) } if (e.tsa !== undefined) { this.dTsa = new a(e.tsa) } } }; YAHOO.lang.extend(KJUR.asn1.tsp.TSTInfo, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.TimeStampResp = function (g) { var e = KJUR, d = e.asn1, c = d.DERSequence, f = d.ASN1Object, a = d.tsp, b = a.PKIStatusInfo; a.TimeStampResp.superclass.constructor.call(this); this.dStatus = null; this.dTST = null; this.getEncodedHex = function () { if (this.dStatus == null) { throw \"status shall be specified\" } var h = [this.dStatus]; if (this.dTST != null) { h.push(this.dTST) } var i = new c({ array: h }); this.hTLV = i.getEncodedHex(); return this.hTLV }; if (g !== undefined) { if (typeof g.status == \"object\") { this.dStatus = new b(g.status) } if (g.tst !== undefined && g.tst instanceof f) { this.dTST = g.tst.getContentInfo() } } }; YAHOO.lang.extend(KJUR.asn1.tsp.TimeStampResp, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.PKIStatusInfo = function (h) { var g = KJUR, f = g.asn1, e = f.DERSequence, a = f.tsp, d = a.PKIStatus, c = a.PKIFreeText, b = a.PKIFailureInfo; a.PKIStatusInfo.superclass.constructor.call(this); this.dStatus = null; this.dStatusString = null; this.dFailureInfo = null; this.getEncodedHex = function () { if (this.dStatus == null) { throw \"status shall be specified\" } var i = [this.dStatus]; if (this.dStatusString != null) { i.push(this.dStatusString) } if (this.dFailureInfo != null) { i.push(this.dFailureInfo) } var j = new e({ array: i }); this.hTLV = j.getEncodedHex(); return this.hTLV }; if (h !== undefined) { if (typeof h.status == \"object\") { this.dStatus = new d(h.status) } if (typeof h.statstr == \"object\") { this.dStatusString = new c({ array: h.statstr }) } if (typeof h.failinfo == \"object\") { this.dFailureInfo = new b(h.failinfo) } } }; YAHOO.lang.extend(KJUR.asn1.tsp.PKIStatusInfo, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.PKIStatus = function (h) { var d = KJUR, c = d.asn1, g = c.DERInteger, a = c.tsp, b = a.PKIStatus; a.PKIStatus.superclass.constructor.call(this); var f = null; this.getEncodedHex = function () { this.hTLV = this.dStatus.getEncodedHex(); return this.hTLV }; if (h !== undefined) { if (h.name !== undefined) { var e = b.valueList; if (e[h.name] === undefined) { throw \"name undefined: \" + h.name } this.dStatus = new g({ \"int\": e[h.name] }) } else { this.dStatus = new g(h) } } }; YAHOO.lang.extend(KJUR.asn1.tsp.PKIStatus, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.PKIStatus.valueList = { granted: 0, grantedWithMods: 1, rejection: 2, waiting: 3, revocationWarning: 4, revocationNotification: 5 }; KJUR.asn1.tsp.PKIFreeText = function (f) { var e = KJUR, d = e.asn1, b = d.DERSequence, c = d.DERUTF8String, a = d.tsp; a.PKIFreeText.superclass.constructor.call(this); this.textList = []; this.getEncodedHex = function () { var g = []; for (var j = 0; j < this.textList.length; j++) { g.push(new c({ str: this.textList[j] })) } var h = new b({ array: g }); this.hTLV = h.getEncodedHex(); return this.hTLV }; if (f !== undefined) { if (typeof f.array == \"object\") { this.textList = f.array } } }; YAHOO.lang.extend(KJUR.asn1.tsp.PKIFreeText, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.PKIFailureInfo = function (g) { var d = KJUR, c = d.asn1, f = c.DERBitString, a = c.tsp, b = a.PKIFailureInfo; b.superclass.constructor.call(this); this.value = null; this.getEncodedHex = function () { if (this.value == null) { throw \"value shall be specified\" } var h = new Number(this.value).toString(2); var i = new f(); i.setByBinaryString(h); this.hTLV = i.getEncodedHex(); return this.hTLV }; if (g !== undefined) { if (typeof g.name == \"string\") { var e = b.valueList; if (e[g.name] === undefined) { throw \"name undefined: \" + g.name } this.value = e[g.name] } else { if (typeof g[\"int\"] == \"number\") { this.value = g[\"int\"] } } } }; YAHOO.lang.extend(KJUR.asn1.tsp.PKIFailureInfo, KJUR.asn1.ASN1Object); KJUR.asn1.tsp.PKIFailureInfo.valueList = { badAlg: 0, badRequest: 2, badDataFormat: 5, timeNotAvailable: 14, unacceptedPolicy: 15, unacceptedExtension: 16, addInfoNotAvailable: 17, systemFailure: 25 }; KJUR.asn1.tsp.AbstractTSAAdapter = function (a) { this.getTSTHex = function (c, b) { throw \"not implemented yet\" } }; KJUR.asn1.tsp.SimpleTSAAdapter = function (e) { var d = KJUR, c = d.asn1, a = c.tsp, b = d.crypto.Util.hashHex; a.SimpleTSAAdapter.superclass.constructor.call(this); this.params = null; this.serial = 0; this.getTSTHex = function (g, f) { var i = b(g, f); this.params.tstInfo.messageImprint = { hashAlg: f, hashValue: i }; this.params.tstInfo.serialNumber = { \"int\": this.serial++ }; var h = Math.floor(Math.random() * 1000000000); this.params.tstInfo.nonce = { \"int\": h }; var j = a.TSPUtil.newTimeStampToken(this.params); return j.getContentInfoEncodedHex() }; if (e !== undefined) { this.params = e } }; YAHOO.lang.extend(KJUR.asn1.tsp.SimpleTSAAdapter, KJUR.asn1.tsp.AbstractTSAAdapter); KJUR.asn1.tsp.FixedTSAAdapter = function (e) { var d = KJUR, c = d.asn1, a = c.tsp, b = d.crypto.Util.hashHex; a.FixedTSAAdapter.superclass.constructor.call(this); this.params = null; this.getTSTHex = function (g, f) { var h = b(g, f); this.params.tstInfo.messageImprint = { hashAlg: f, hashValue: h }; var i = a.TSPUtil.newTimeStampToken(this.params); return i.getContentInfoEncodedHex() }; if (e !== undefined) { this.params = e } }; YAHOO.lang.extend(KJUR.asn1.tsp.FixedTSAAdapter, KJUR.asn1.tsp.AbstractTSAAdapter); KJUR.asn1.tsp.TSPUtil = new function () { }; KJUR.asn1.tsp.TSPUtil.newTimeStampToken = function (c) { var b = KJUR, h = b.asn1, m = h.cms, k = h.tsp, a = h.tsp.TSTInfo; var j = new m.SignedData(); var g = new a(c.tstInfo); var f = g.getEncodedHex(); j.dEncapContentInfo.setContentValue({ hex: f }); j.dEncapContentInfo.setContentType(\"tstinfo\"); if (typeof c.certs == \"object\") { for (var e = 0; e < c.certs.length; e++) { j.addCertificatesByPEM(c.certs[e]) } } var d = j.signerInfoList[0]; d.setSignerIdentifier(c.signerCert); d.setForContentAndHash({ sdObj: j, eciObj: j.dEncapContentInfo, hashAlg: c.hashAlg }); var l = new m.SigningCertificate({ array: [c.signerCert] }); d.dSignedAttrs.add(l); d.sign(c.signerPrvKey, c.sigAlg); return j }; KJUR.asn1.tsp.TSPUtil.parseTimeStampReq = function (m) { var l = ASN1HEX; var h = l.getChildIdx; var f = l.getV; var b = l.getTLV; var j = {}; j.certreq = false; var a = h(m, 0); if (a.length < 2) { throw \"TimeStampReq must have at least 2 items\" } var e = b(m, a[1]); j.mi = KJUR.asn1.tsp.TSPUtil.parseMessageImprint(e); for (var d = 2; d < a.length; d++) { var g = a[d]; var k = m.substr(g, 2); if (k == \"06\") { var c = f(m, g); j.policy = l.hextooidstr(c) } if (k == \"02\") { j.nonce = f(m, g) } if (k == \"01\") { j.certreq = true } } return j }; KJUR.asn1.tsp.TSPUtil.parseMessageImprint = function (c) { var m = ASN1HEX; var j = m.getChildIdx; var i = m.getV; var g = m.getIdxbyList; var k = {}; if (c.substr(0, 2) != \"30\") { throw \"head of messageImprint hex shall be '30'\" } var a = j(c, 0); var l = g(c, 0, [0, 0]); var e = i(c, l); var d = m.hextooidstr(e); var h = KJUR.asn1.x509.OID.oid2name(d); if (h == \"\") { throw \"hashAlg name undefined: \" + d } var b = h; var f = g(c, 0, [1]); k.hashAlg = b; k.hashValue = i(c, f); return k };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1) { KJUR.asn1 = {} } if (typeof KJUR.asn1.cades == \"undefined\" || !KJUR.asn1.cades) { KJUR.asn1.cades = {} } KJUR.asn1.cades.SignaturePolicyIdentifier = function (f) { var b = KJUR, h = b.asn1, i = h.DERObjectIdentifier, g = h.DERSequence, e = h.cades, c = e.OtherHashAlgAndValue; e.SignaturePolicyIdentifier.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.16.2.15\"; if (f !== undefined) { if (typeof f.oid == \"string\" && typeof f.hash == \"object\") { var d = new i({ oid: f.oid }); var a = new c(f.hash); var j = new g({ array: [d, a] }); this.valueList = [j] } } }; YAHOO.lang.extend(KJUR.asn1.cades.SignaturePolicyIdentifier, KJUR.asn1.cms.Attribute); KJUR.asn1.cades.OtherHashAlgAndValue = function (e) { var a = KJUR, g = a.asn1, f = g.DERSequence, h = g.DEROctetString, d = g.x509, i = d.AlgorithmIdentifier, c = g.cades, b = c.OtherHashAlgAndValue; b.superclass.constructor.call(this); this.dAlg = null; this.dHash = null; this.getEncodedHex = function () { var j = new f({ array: [this.dAlg, this.dHash] }); this.hTLV = j.getEncodedHex(); return this.hTLV }; if (e !== undefined) { if (typeof e.alg == \"string\" && typeof e.hash == \"string\") { this.dAlg = new i({ name: e.alg }); this.dHash = new h({ hex: e.hash }) } } }; YAHOO.lang.extend(KJUR.asn1.cades.OtherHashAlgAndValue, KJUR.asn1.ASN1Object); KJUR.asn1.cades.SignatureTimeStamp = function (h) { var c = KJUR, b = c.asn1, e = b.ASN1Object, g = b.x509, a = b.cades; a.SignatureTimeStamp.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.16.2.14\"; this.tstHex = null; if (h !== undefined) { if (h.res !== undefined) { if (typeof h.res == \"string\" && h.res.match(/^[0-9A-Fa-f]+$/)) { } else { if (h.res instanceof e) { } else { throw \"res param shall be ASN1Object or hex string\" } } } if (h.tst !== undefined) { if (typeof h.tst == \"string\" && h.tst.match(/^[0-9A-Fa-f]+$/)) { var f = new e(); this.tstHex = h.tst; f.hTLV = this.tstHex; f.getEncodedHex(); this.valueList = [f] } else { if (h.tst instanceof e) { } else { throw \"tst param shall be ASN1Object or hex string\" } } } } }; YAHOO.lang.extend(KJUR.asn1.cades.SignatureTimeStamp, KJUR.asn1.cms.Attribute); KJUR.asn1.cades.CompleteCertificateRefs = function (d) { var c = KJUR, b = c.asn1, a = b.cades; a.CompleteCertificateRefs.superclass.constructor.call(this); this.attrTypeOid = \"1.2.840.113549.1.9.16.2.21\"; this.setByArray = function (e) { this.valueList = []; for (var f = 0; f < e.length; f++) { var g = new a.OtherCertID(e[f]); this.valueList.push(g) } }; if (d !== undefined) { if (typeof d == \"object\" && typeof d.length == \"number\") { this.setByArray(d) } } }; YAHOO.lang.extend(KJUR.asn1.cades.CompleteCertificateRefs, KJUR.asn1.cms.Attribute); KJUR.asn1.cades.OtherCertID = function (e) { var c = KJUR, b = c.asn1, d = b.cms, a = b.cades; a.OtherCertID.superclass.constructor.call(this); this.hasIssuerSerial = true; this.dOtherCertHash = null; this.dIssuerSerial = null; this.setByCertPEM = function (f) { this.dOtherCertHash = new a.OtherHash(f); if (this.hasIssuerSerial) { this.dIssuerSerial = new d.IssuerAndSerialNumber(f) } }; this.getEncodedHex = function () { if (this.hTLV != null) { return this.hTLV } if (this.dOtherCertHash == null) { throw \"otherCertHash not set\" } var f = [this.dOtherCertHash]; if (this.dIssuerSerial != null) { f.push(this.dIssuerSerial) } var g = new b.DERSequence({ array: f }); this.hTLV = g.getEncodedHex(); return this.hTLV }; if (e !== undefined) { if (typeof e == \"string\" && e.indexOf(\"-----BEGIN \") != -1) { this.setByCertPEM(e) } if (typeof e == \"object\") { if (e.hasis === false) { this.hasIssuerSerial = false } if (typeof e.cert == \"string\") { this.setByCertPEM(e.cert) } } } }; YAHOO.lang.extend(KJUR.asn1.cades.OtherCertID, KJUR.asn1.ASN1Object); KJUR.asn1.cades.OtherHash = function (f) { var d = KJUR, c = d.asn1, e = c.cms, b = c.cades, g = b.OtherHashAlgAndValue, a = d.crypto.Util.hashHex; b.OtherHash.superclass.constructor.call(this); this.alg = \"sha256\"; this.dOtherHash = null; this.setByCertPEM = function (h) { if (h.indexOf(\"-----BEGIN \") == -1) { throw \"certPEM not to seem PEM format\" } var i = pemtohex(h); var j = a(i, this.alg); this.dOtherHash = new g({ alg: this.alg, hash: j }) }; this.getEncodedHex = function () { if (this.dOtherHash == null) { throw \"OtherHash not set\" } return this.dOtherHash.getEncodedHex() }; if (f !== undefined) { if (typeof f == \"string\") { if (f.indexOf(\"-----BEGIN \") != -1) { this.setByCertPEM(f) } else { if (f.match(/^[0-9A-Fa-f]+$/)) { this.dOtherHash = new c.DEROctetString({ hex: f }) } else { throw \"unsupported string value for params\" } } } else { if (typeof f == \"object\") { if (typeof f.cert == \"string\") { if (typeof f.alg == \"string\") { this.alg = f.alg } this.setByCertPEM(f.cert) } else { this.dOtherHash = new g(f) } } } } }; YAHOO.lang.extend(KJUR.asn1.cades.OtherHash, KJUR.asn1.ASN1Object); KJUR.asn1.cades.CAdESUtil = new function () { }; KJUR.asn1.cades.CAdESUtil.addSigTS = function (c, b, a) { }; KJUR.asn1.cades.CAdESUtil.parseSignedDataForAddingUnsigned = function (e) { var p = ASN1HEX, u = p.getChildIdx, b = p.getTLV, a = p.getTLVbyList, k = p.getIdxbyList, A = KJUR, g = A.asn1, l = g.ASN1Object, j = g.cms, h = j.SignedData, v = g.cades, z = v.CAdESUtil; var m = {}; if (a(e, 0, [0]) != \"06092a864886f70d010702\") { throw \"hex is not CMS SignedData\" } var y = k(e, 0, [1, 0]); var B = u(e, y); if (B.length < 4) { throw \"num of SignedData elem shall be 4 at least\" } var d = B.shift(); m.version = b(e, d); var w = B.shift(); m.algs = b(e, w); var c = B.shift(); m.encapcontent = b(e, c); m.certs = null; m.revs = null; m.si = []; var o = B.shift(); if (e.substr(o, 2) == \"a0\") { m.certs = b(e, o); o = B.shift() } if (e.substr(o, 2) == \"a1\") { m.revs = b(e, o); o = B.shift() } var t = o; if (e.substr(t, 2) != \"31\") { throw \"Can't find signerInfos\" } var f = u(e, t); for (var q = 0; q < f.length; q++) { var s = f[q]; var n = z.parseSignerInfoForAddingUnsigned(e, s, q); m.si[q] = n } var x = null; m.obj = new h(); x = new l(); x.hTLV = m.version; m.obj.dCMSVersion = x; x = new l(); x.hTLV = m.algs; m.obj.dDigestAlgs = x; x = new l(); x.hTLV = m.encapcontent; m.obj.dEncapContentInfo = x; x = new l(); x.hTLV = m.certs; m.obj.dCerts = x; m.obj.signerInfoList = []; for (var q = 0; q < m.si.length; q++) { m.obj.signerInfoList.push(m.si[q].obj) } return m }; KJUR.asn1.cades.CAdESUtil.parseSignerInfoForAddingUnsigned = function (g, q, c) { var p = ASN1HEX, s = p.getChildIdx, a = p.getTLV, l = p.getV, v = KJUR, h = v.asn1, n = h.ASN1Object, j = h.cms, k = j.AttributeList, w = j.SignerInfo; var o = {}; var t = s(g, q); if (t.length != 6) { throw \"not supported items for SignerInfo (!=6)\" } var d = t.shift(); o.version = a(g, d); var e = t.shift(); o.si = a(g, e); var m = t.shift(); o.digalg = a(g, m); var f = t.shift(); o.sattrs = a(g, f); var i = t.shift(); o.sigalg = a(g, i); var b = t.shift(); o.sig = a(g, b); o.sigval = l(g, b); var u = null; o.obj = new w(); u = new n(); u.hTLV = o.version; o.obj.dCMSVersion = u; u = new n(); u.hTLV = o.si; o.obj.dSignerIdentifier = u; u = new n(); u.hTLV = o.digalg; o.obj.dDigestAlgorithm = u; u = new n(); u.hTLV = o.sattrs; o.obj.dSignedAttrs = u; u = new n(); u.hTLV = o.sigalg; o.obj.dSigAlg = u; u = new n(); u.hTLV = o.sig; o.obj.dSig = u; o.obj.dUnsignedAttrs = new k(); return o };\nif (typeof KJUR.asn1.csr == \"undefined\" || !KJUR.asn1.csr) { KJUR.asn1.csr = {} } KJUR.asn1.csr.CertificationRequest = function (d) { var a = KJUR, f = a.asn1, b = f.DERBitString, e = f.DERSequence, k = f.csr, c = f.x509; k.CertificationRequest.superclass.constructor.call(this); var l = null; var j = null; var h = null; var i = null; var g = null; this.sign = function (o, n) { if (this.prvKey == null) { this.prvKey = n } this.asn1SignatureAlg = new c.AlgorithmIdentifier({ name: o }); sig = new a.crypto.Signature({ alg: o }); sig.initSign(this.prvKey); sig.updateHex(this.asn1CSRInfo.getEncodedHex()); this.hexSig = sig.sign(); this.asn1Sig = new b({ hex: \"00\" + this.hexSig }); var m = new e({ array: [this.asn1CSRInfo, this.asn1SignatureAlg, this.asn1Sig] }); this.hTLV = m.getEncodedHex(); this.isModified = false }; this.getPEMString = function () { return hextopem(this.getEncodedHex(), \"CERTIFICATE REQUEST\") }; this.getEncodedHex = function () { if (this.isModified == false && this.hTLV != null) { return this.hTLV } throw \"not signed yet\" }; if (d !== undefined && d.csrinfo !== undefined) { this.asn1CSRInfo = d.csrinfo } }; YAHOO.lang.extend(KJUR.asn1.csr.CertificationRequest, KJUR.asn1.ASN1Object); KJUR.asn1.csr.CertificationRequestInfo = function (e) { var b = KJUR, h = b.asn1, g = h.DERInteger, f = h.DERSequence, m = h.DERSet, j = h.DERNull, c = h.DERTaggedObject, k = h.DERObjectIdentifier, l = h.csr, d = h.x509, a = d.X500Name, n = d.Extension, i = KEYUTIL; l.CertificationRequestInfo.superclass.constructor.call(this); this._initialize = function () { this.asn1Array = new Array(); this.asn1Version = new g({ \"int\": 0 }); this.asn1Subject = null; this.asn1SubjPKey = null; this.extensionsArray = new Array() }; this.setSubjectByParam = function (o) { this.asn1Subject = new a(o) }; this.setSubjectPublicKeyByGetKey = function (p) { var o = i.getKey(p); this.asn1SubjPKey = new d.SubjectPublicKeyInfo(o) }; this.appendExtensionByName = function (p, o) { n.appendByNameToArray(p, o, this.extensionsArray) }; this.getEncodedHex = function () { this.asn1Array = new Array(); this.asn1Array.push(this.asn1Version); this.asn1Array.push(this.asn1Subject); this.asn1Array.push(this.asn1SubjPKey); if (this.extensionsArray.length > 0) { var s = new f({ array: this.extensionsArray }); var r = new m({ array: [s] }); var q = new f({ array: [new k({ oid: \"1.2.840.113549.1.9.14\" }), r] }); var p = new c({ explicit: true, tag: \"a0\", obj: q }); this.asn1Array.push(p) } else { var p = new c({ explicit: false, tag: \"a0\", obj: new j() }); this.asn1Array.push(p) } var t = new f({ array: this.asn1Array }); this.hTLV = t.getEncodedHex(); this.isModified = false; return this.hTLV }; this._initialize() }; YAHOO.lang.extend(KJUR.asn1.csr.CertificationRequestInfo, KJUR.asn1.ASN1Object); KJUR.asn1.csr.CSRUtil = new function () { }; KJUR.asn1.csr.CSRUtil.newCSRPEM = function (h) { var c = KEYUTIL, b = KJUR.asn1.csr; if (h.subject === undefined) { throw \"parameter subject undefined\" } if (h.sbjpubkey === undefined) { throw \"parameter sbjpubkey undefined\" } if (h.sigalg === undefined) { throw \"parameter sigalg undefined\" } if (h.sbjprvkey === undefined) { throw \"parameter sbjpubkey undefined\" } var d = new b.CertificationRequestInfo(); d.setSubjectByParam(h.subject); d.setSubjectPublicKeyByGetKey(h.sbjpubkey); if (h.ext !== undefined && h.ext.length !== undefined) { for (var e = 0; e < h.ext.length; e++) { for (key in h.ext[e]) { d.appendExtensionByName(key, h.ext[e][key]) } } } var f = new b.CertificationRequest({ csrinfo: d }); var a = c.getKey(h.sbjprvkey); f.sign(h.sigalg, a); var g = f.getPEMString(); return g }; KJUR.asn1.csr.CSRUtil.getInfo = function (b) { var d = ASN1HEX; var e = d.getTLVbyList; var a = {}; a.subject = {}; a.pubkey = {}; if (b.indexOf(\"-----BEGIN CERTIFICATE REQUEST\") == -1) { throw \"argument is not PEM file\" } var c = pemtohex(b, \"CERTIFICATE REQUEST\"); a.subject.hex = e(c, 0, [0, 1]); a.subject.name = X509.hex2dn(a.subject.hex); a.pubkey.hex = e(c, 0, [0, 2]); a.pubkey.obj = KEYUTIL.getKey(a.pubkey.hex, null, \"pkcs8pub\"); return a };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.asn1 == \"undefined\" || !KJUR.asn1) { KJUR.asn1 = {} } if (typeof KJUR.asn1.ocsp == \"undefined\" || !KJUR.asn1.ocsp) { KJUR.asn1.ocsp = {} } KJUR.asn1.ocsp.DEFAULT_HASH = \"sha1\"; KJUR.asn1.ocsp.CertID = function (g) { var d = KJUR, k = d.asn1, m = k.DEROctetString, j = k.DERInteger, h = k.DERSequence, f = k.x509, n = f.AlgorithmIdentifier, o = k.ocsp, l = o.DEFAULT_HASH, i = d.crypto, e = i.Util.hashHex, c = X509, q = ASN1HEX; o.CertID.superclass.constructor.call(this); this.dHashAlg = null; this.dIssuerNameHash = null; this.dIssuerKeyHash = null; this.dSerialNumber = null; this.setByValue = function (t, s, p, r) { if (r === undefined) { r = l } this.dHashAlg = new n({ name: r }); this.dIssuerNameHash = new m({ hex: t }); this.dIssuerKeyHash = new m({ hex: s }); this.dSerialNumber = new j({ hex: p }) }; this.setByCert = function (x, t, v) { if (v === undefined) { v = l } var p = new c(); p.readCertPEM(t); var y = new c(); y.readCertPEM(x); var z = y.getPublicKeyHex(); var w = q.getTLVbyList(z, 0, [1, 0], \"30\"); var r = p.getSerialNumberHex(); var s = e(y.getSubjectHex(), v); var u = e(w, v); this.setByValue(s, u, r, v); this.hoge = p.getSerialNumberHex() }; this.getEncodedHex = function () { if (this.dHashAlg === null && this.dIssuerNameHash === null && this.dIssuerKeyHash === null && this.dSerialNumber === null) { throw \"not yet set values\" } var p = [this.dHashAlg, this.dIssuerNameHash, this.dIssuerKeyHash, this.dSerialNumber]; var r = new h({ array: p }); this.hTLV = r.getEncodedHex(); return this.hTLV }; if (g !== undefined) { var b = g; if (b.issuerCert !== undefined && b.subjectCert !== undefined) { var a = l; if (b.alg === undefined) { a = undefined } this.setByCert(b.issuerCert, b.subjectCert, a) } else { if (b.namehash !== undefined && b.keyhash !== undefined && b.serial !== undefined) { var a = l; if (b.alg === undefined) { a = undefined } this.setByValue(b.namehash, b.keyhash, b.serial, a) } else { throw \"invalid constructor arguments\" } } } }; YAHOO.lang.extend(KJUR.asn1.ocsp.CertID, KJUR.asn1.ASN1Object); KJUR.asn1.ocsp.Request = function (f) { var c = KJUR, b = c.asn1, a = b.DERSequence, d = b.ocsp; d.Request.superclass.constructor.call(this); this.dReqCert = null; this.dExt = null; this.getEncodedHex = function () { var g = []; if (this.dReqCert === null) { throw \"reqCert not set\" } g.push(this.dReqCert); var h = new a({ array: g }); this.hTLV = h.getEncodedHex(); return this.hTLV }; if (typeof f !== \"undefined\") { var e = new d.CertID(f); this.dReqCert = e } }; YAHOO.lang.extend(KJUR.asn1.ocsp.Request, KJUR.asn1.ASN1Object); KJUR.asn1.ocsp.TBSRequest = function (e) { var c = KJUR, b = c.asn1, a = b.DERSequence, d = b.ocsp; d.TBSRequest.superclass.constructor.call(this); this.version = 0; this.dRequestorName = null; this.dRequestList = []; this.dRequestExt = null; this.setRequestListByParam = function (h) { var f = []; for (var g = 0; g < h.length; g++) { var j = new d.Request(h[0]); f.push(j) } this.dRequestList = f }; this.getEncodedHex = function () { var f = []; if (this.version !== 0) { throw \"not supported version: \" + this.version } if (this.dRequestorName !== null) { throw \"requestorName not supported\" } var h = new a({ array: this.dRequestList }); f.push(h); if (this.dRequestExt !== null) { throw \"requestExtensions not supported\" } var g = new a({ array: f }); this.hTLV = g.getEncodedHex(); return this.hTLV }; if (e !== undefined) { if (e.reqList !== undefined) { this.setRequestListByParam(e.reqList) } } }; YAHOO.lang.extend(KJUR.asn1.ocsp.TBSRequest, KJUR.asn1.ASN1Object); KJUR.asn1.ocsp.OCSPRequest = function (f) { var c = KJUR, b = c.asn1, a = b.DERSequence, d = b.ocsp; d.OCSPRequest.superclass.constructor.call(this); this.dTbsRequest = null; this.dOptionalSignature = null; this.getEncodedHex = function () { var g = []; if (this.dTbsRequest !== null) { g.push(this.dTbsRequest) } else { throw \"tbsRequest not set\" } if (this.dOptionalSignature !== null) { throw \"optionalSignature not supported\" } var h = new a({ array: g }); this.hTLV = h.getEncodedHex(); return this.hTLV }; if (f !== undefined) { if (f.reqList !== undefined) { var e = new d.TBSRequest(f); this.dTbsRequest = e } } }; YAHOO.lang.extend(KJUR.asn1.ocsp.OCSPRequest, KJUR.asn1.ASN1Object); KJUR.asn1.ocsp.OCSPUtil = {}; KJUR.asn1.ocsp.OCSPUtil.getRequestHex = function (a, b, h) { var d = KJUR, c = d.asn1, e = c.ocsp; if (h === undefined) { h = e.DEFAULT_HASH } var g = { alg: h, issuerCert: a, subjectCert: b }; var f = new e.OCSPRequest({ reqList: [g] }); return f.getEncodedHex() }; KJUR.asn1.ocsp.OCSPUtil.getOCSPResponseInfo = function (b) { var k = ASN1HEX; var c = k.getVbyList; var d = k.getIdxbyList; var c = k.getVbyList; var f = k.getV; var l = {}; try { var i = c(b, 0, [0], \"0a\"); l.responseStatus = parseInt(i, 16) } catch (e) { } if (l.responseStatus !== 0) { return l } try { var g = d(b, 0, [1, 0, 1, 0, 0, 2, 0, 1]); if (b.substr(g, 2) === \"80\") { l.certStatus = \"good\" } else { if (b.substr(g, 2) === \"a1\") { l.certStatus = \"revoked\"; l.revocationTime = hextoutf8(c(b, g, [0])) } else { if (b.substr(g, 2) === \"82\") { l.certStatus = \"unknown\" } } } } catch (e) { } try { var a = d(b, 0, [1, 0, 1, 0, 0, 2, 0, 2]); l.thisUpdate = hextoutf8(f(b, a)) } catch (e) { } try { var j = d(b, 0, [1, 0, 1, 0, 0, 2, 0, 3]); if (b.substr(j, 2) === \"a0\") { l.nextUpdate = hextoutf8(c(b, j, [0])) } } catch (e) { } return l };\nvar KJUR; if (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.lang == \"undefined\" || !KJUR.lang) { KJUR.lang = {} } KJUR.lang.String = function () { }; function Base64x() { } function stoBA(d) { var b = new Array(); for (var c = 0; c < d.length; c++) { b[c] = d.charCodeAt(c) } return b } function BAtos(b) { var d = \"\"; for (var c = 0; c < b.length; c++) { d = d + String.fromCharCode(b[c]) } return d } function BAtohex(b) { var e = \"\"; for (var d = 0; d < b.length; d++) { var c = b[d].toString(16); if (c.length == 1) { c = \"0\" + c } e = e + c } return e } function stohex(a) { return BAtohex(stoBA(a)) } function stob64(a) { return hex2b64(stohex(a)) } function stob64u(a) { return b64tob64u(hex2b64(stohex(a))) } function b64utos(a) { return BAtos(b64toBA(b64utob64(a))) } function b64tob64u(a) { a = a.replace(/\\=/g, \"\"); a = a.replace(/\\+/g, \"-\"); a = a.replace(/\\//g, \"_\"); return a } function b64utob64(a) { if (a.length % 4 == 2) { a = a + \"==\" } else { if (a.length % 4 == 3) { a = a + \"=\" } } a = a.replace(/-/g, \"+\"); a = a.replace(/_/g, \"/\"); return a } function hextob64u(a) { if (a.length % 2 == 1) { a = \"0\" + a } return b64tob64u(hex2b64(a)) } function b64utohex(a) { return b64tohex(b64utob64(a)) } var utf8tob64u, b64utoutf8; if (typeof Buffer === \"function\") { utf8tob64u = function (a) { return b64tob64u(new Buffer(a, \"utf8\").toString(\"base64\")) }; b64utoutf8 = function (a) { return new Buffer(b64utob64(a), \"base64\").toString(\"utf8\") } } else { utf8tob64u = function (a) { return hextob64u(uricmptohex(encodeURIComponentAll(a))) }; b64utoutf8 = function (a) { return decodeURIComponent(hextouricmp(b64utohex(a))) } } function utf8tob64(a) { return hex2b64(uricmptohex(encodeURIComponentAll(a))) } function b64toutf8(a) { return decodeURIComponent(hextouricmp(b64tohex(a))) } function utf8tohex(a) { return uricmptohex(encodeURIComponentAll(a)) } function hextoutf8(a) { return decodeURIComponent(hextouricmp(a)) } function hextorstr(c) { var b = \"\"; for (var a = 0; a < c.length - 1; a += 2) { b += String.fromCharCode(parseInt(c.substr(a, 2), 16)) } return b } function rstrtohex(c) { var a = \"\"; for (var b = 0; b < c.length; b++) { a += (\"0\" + c.charCodeAt(b).toString(16)).slice(-2) } return a } function hextob64(a) { return hex2b64(a) } function hextob64nl(b) { var a = hextob64(b); var c = a.replace(/(.{64})/g, \"$1\\r\\n\"); c = c.replace(/\\r\\n$/, \"\"); return c } function b64nltohex(b) { var a = b.replace(/[^0-9A-Za-z\\/+=]*/g, \"\"); var c = b64tohex(a); return c } function hextopem(a, b) { var c = hextob64nl(a); return \"-----BEGIN \" + b + \"-----\\r\\n\" + c + \"\\r\\n-----END \" + b + \"-----\\r\\n\" } function pemtohex(a, b) { if (a.indexOf(\"-----BEGIN \") == -1) { throw \"can't find PEM header: \" + b } if (b !== undefined) { a = a.replace(\"-----BEGIN \" + b + \"-----\", \"\"); a = a.replace(\"-----END \" + b + \"-----\", \"\") } else { a = a.replace(/-----BEGIN [^-]+-----/, \"\"); a = a.replace(/-----END [^-]+-----/, \"\") } return b64nltohex(a) } function hextoArrayBuffer(d) { if (d.length % 2 != 0) { throw \"input is not even length\" } if (d.match(/^[0-9A-Fa-f]+$/) == null) { throw \"input is not hexadecimal\" } var b = new ArrayBuffer(d.length / 2); var a = new DataView(b); for (var c = 0; c < d.length / 2; c++) { a.setUint8(c, parseInt(d.substr(c * 2, 2), 16)) } return b } function ArrayBuffertohex(b) { var d = \"\"; var a = new DataView(b); for (var c = 0; c < b.byteLength; c++) { d += (\"00\" + a.getUint8(c).toString(16)).slice(-2) } return d } function zulutomsec(n) { var l, j, m, e, f, i, b, k; var a, h, g, c; c = n.match(/^(\\d{2}|\\d{4})(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(|\\.\\d+)Z$/); if (c) { a = c[1]; l = parseInt(a); if (a.length === 2) { if (50 <= l && l < 100) { l = 1900 + l } else { if (0 <= l && l < 50) { l = 2000 + l } } } j = parseInt(c[2]) - 1; m = parseInt(c[3]); e = parseInt(c[4]); f = parseInt(c[5]); i = parseInt(c[6]); b = 0; h = c[7]; if (h !== \"\") { g = (h.substr(1) + \"00\").substr(0, 3); b = parseInt(g) } return Date.UTC(l, j, m, e, f, i, b) } throw \"unsupported zulu format: \" + n } function zulutosec(a) { var b = zulutomsec(a); return ~~(b / 1000) } function zulutodate(a) { return new Date(zulutomsec(a)) } function datetozulu(g, e, f) { var b; var a = g.getUTCFullYear(); if (e) { if (a < 1950 || 2049 < a) { throw \"not proper year for UTCTime: \" + a } b = (\"\" + a).slice(-2) } else { b = (\"000\" + a).slice(-4) } b += (\"0\" + (g.getUTCMonth() + 1)).slice(-2); b += (\"0\" + g.getUTCDate()).slice(-2); b += (\"0\" + g.getUTCHours()).slice(-2); b += (\"0\" + g.getUTCMinutes()).slice(-2); b += (\"0\" + g.getUTCSeconds()).slice(-2); if (f) { var c = g.getUTCMilliseconds(); if (c !== 0) { c = (\"00\" + c).slice(-3); c = c.replace(/0+$/g, \"\"); b += \".\" + c } } b += \"Z\"; return b } function uricmptohex(a) { return a.replace(/%/g, \"\") } function hextouricmp(a) { return a.replace(/(..)/g, \"%$1\") } function encodeURIComponentAll(a) { var d = encodeURIComponent(a); var b = \"\"; for (var c = 0; c < d.length; c++) { if (d[c] == \"%\") { b = b + d.substr(c, 3); c = c + 2 } else { b = b + \"%\" + stohex(d[c]) } } return b } function newline_toUnix(a) { a = a.replace(/\\r\\n/mg, \"\\n\"); return a } function newline_toDos(a) { a = a.replace(/\\r\\n/mg, \"\\n\"); a = a.replace(/\\n/mg, \"\\r\\n\"); return a } KJUR.lang.String.isInteger = function (a) { if (a.match(/^[0-9]+$/)) { return true } else { if (a.match(/^-[0-9]+$/)) { return true } else { return false } } }; KJUR.lang.String.isHex = function (a) { if (a.length % 2 == 0 && (a.match(/^[0-9a-f]+$/) || a.match(/^[0-9A-F]+$/))) { return true } else { return false } }; KJUR.lang.String.isBase64 = function (a) { a = a.replace(/\\s+/g, \"\"); if (a.match(/^[0-9A-Za-z+\\/]+={0,3}$/) && a.length % 4 == 0) { return true } else { return false } }; KJUR.lang.String.isBase64URL = function (a) { if (a.match(/[+/=]/)) { return false } a = b64utob64(a); return KJUR.lang.String.isBase64(a) }; KJUR.lang.String.isIntegerArray = function (a) { a = a.replace(/\\s+/g, \"\"); if (a.match(/^\\[[0-9,]+\\]$/)) { return true } else { return false } }; function hextoposhex(a) { if (a.length % 2 == 1) { return \"0\" + a } if (a.substr(0, 1) > \"7\") { return \"00\" + a } return a } function intarystrtohex(b) { b = b.replace(/^\\s*\\[\\s*/, \"\"); b = b.replace(/\\s*\\]\\s*$/, \"\"); b = b.replace(/\\s*/g, \"\"); try { var c = b.split(/,/).map(function (g, e, h) { var f = parseInt(g); if (f < 0 || 255 < f) { throw \"integer not in range 0-255\" } var d = (\"00\" + f.toString(16)).slice(-2); return d }).join(\"\"); return c } catch (a) { throw \"malformed integer array string: \" + a } } var strdiffidx = function (c, a) { var d = c.length; if (c.length > a.length) { d = a.length } for (var b = 0; b < d; b++) { if (c.charCodeAt(b) != a.charCodeAt(b)) { return b } } if (c.length != a.length) { return d } return -1 };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.crypto == \"undefined\" || !KJUR.crypto) { KJUR.crypto = {} } KJUR.crypto.Util = new function () { this.DIGESTINFOHEAD = { sha1: \"3021300906052b0e03021a05000414\", sha224: \"302d300d06096086480165030402040500041c\", sha256: \"3031300d060960864801650304020105000420\", sha384: \"3041300d060960864801650304020205000430\", sha512: \"3051300d060960864801650304020305000440\", md2: \"3020300c06082a864886f70d020205000410\", md5: \"3020300c06082a864886f70d020505000410\", ripemd160: \"3021300906052b2403020105000414\", }; this.DEFAULTPROVIDER = { md5: \"cryptojs\", sha1: \"cryptojs\", sha224: \"cryptojs\", sha256: \"cryptojs\", sha384: \"cryptojs\", sha512: \"cryptojs\", ripemd160: \"cryptojs\", hmacmd5: \"cryptojs\", hmacsha1: \"cryptojs\", hmacsha224: \"cryptojs\", hmacsha256: \"cryptojs\", hmacsha384: \"cryptojs\", hmacsha512: \"cryptojs\", hmacripemd160: \"cryptojs\", MD5withRSA: \"cryptojs/jsrsa\", SHA1withRSA: \"cryptojs/jsrsa\", SHA224withRSA: \"cryptojs/jsrsa\", SHA256withRSA: \"cryptojs/jsrsa\", SHA384withRSA: \"cryptojs/jsrsa\", SHA512withRSA: \"cryptojs/jsrsa\", RIPEMD160withRSA: \"cryptojs/jsrsa\", MD5withECDSA: \"cryptojs/jsrsa\", SHA1withECDSA: \"cryptojs/jsrsa\", SHA224withECDSA: \"cryptojs/jsrsa\", SHA256withECDSA: \"cryptojs/jsrsa\", SHA384withECDSA: \"cryptojs/jsrsa\", SHA512withECDSA: \"cryptojs/jsrsa\", RIPEMD160withECDSA: \"cryptojs/jsrsa\", SHA1withDSA: \"cryptojs/jsrsa\", SHA224withDSA: \"cryptojs/jsrsa\", SHA256withDSA: \"cryptojs/jsrsa\", MD5withRSAandMGF1: \"cryptojs/jsrsa\", SHA1withRSAandMGF1: \"cryptojs/jsrsa\", SHA224withRSAandMGF1: \"cryptojs/jsrsa\", SHA256withRSAandMGF1: \"cryptojs/jsrsa\", SHA384withRSAandMGF1: \"cryptojs/jsrsa\", SHA512withRSAandMGF1: \"cryptojs/jsrsa\", RIPEMD160withRSAandMGF1: \"cryptojs/jsrsa\", }; this.CRYPTOJSMESSAGEDIGESTNAME = { md5: CryptoJS.algo.MD5, sha1: CryptoJS.algo.SHA1, sha224: CryptoJS.algo.SHA224, sha256: CryptoJS.algo.SHA256, sha384: CryptoJS.algo.SHA384, sha512: CryptoJS.algo.SHA512, ripemd160: CryptoJS.algo.RIPEMD160 }; this.getDigestInfoHex = function (a, b) { if (typeof this.DIGESTINFOHEAD[b] == \"undefined\") { throw \"alg not supported in Util.DIGESTINFOHEAD: \" + b } return this.DIGESTINFOHEAD[b] + a }; this.getPaddedDigestInfoHex = function (h, a, j) { var c = this.getDigestInfoHex(h, a); var d = j / 4; if (c.length + 22 > d) { throw \"key is too short for SigAlg: keylen=\" + j + \",\" + a } var b = \"0001\"; var k = \"00\" + c; var g = \"\"; var l = d - b.length - k.length; for (var f = 0; f < l; f += 2) { g += \"ff\" } var e = b + g + k; return e }; this.hashString = function (a, c) { var b = new KJUR.crypto.MessageDigest({ alg: c }); return b.digestString(a) }; this.hashHex = function (b, c) { var a = new KJUR.crypto.MessageDigest({ alg: c }); return a.digestHex(b) }; this.sha1 = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"sha1\", prov: \"cryptojs\" }); return b.digestString(a) }; this.sha256 = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"sha256\", prov: \"cryptojs\" }); return b.digestString(a) }; this.sha256Hex = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"sha256\", prov: \"cryptojs\" }); return b.digestHex(a) }; this.sha512 = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"sha512\", prov: \"cryptojs\" }); return b.digestString(a) }; this.sha512Hex = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"sha512\", prov: \"cryptojs\" }); return b.digestHex(a) } }; KJUR.crypto.Util.md5 = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"md5\", prov: \"cryptojs\" }); return b.digestString(a) }; KJUR.crypto.Util.ripemd160 = function (a) { var b = new KJUR.crypto.MessageDigest({ alg: \"ripemd160\", prov: \"cryptojs\" }); return b.digestString(a) }; KJUR.crypto.Util.SECURERANDOMGEN = new SecureRandom(); KJUR.crypto.Util.getRandomHexOfNbytes = function (b) { var a = new Array(b); KJUR.crypto.Util.SECURERANDOMGEN.nextBytes(a); return BAtohex(a) }; KJUR.crypto.Util.getRandomBigIntegerOfNbytes = function (a) { return new BigInteger(KJUR.crypto.Util.getRandomHexOfNbytes(a), 16) }; KJUR.crypto.Util.getRandomHexOfNbits = function (d) { var c = d % 8; var a = (d - c) / 8; var b = new Array(a + 1); KJUR.crypto.Util.SECURERANDOMGEN.nextBytes(b); b[0] = (((255 << c) & 255) ^ 255) & b[0]; return BAtohex(b) }; KJUR.crypto.Util.getRandomBigIntegerOfNbits = function (a) { return new BigInteger(KJUR.crypto.Util.getRandomHexOfNbits(a), 16) }; KJUR.crypto.Util.getRandomBigIntegerZeroToMax = function (b) { var a = b.bitLength(); while (1) { var c = KJUR.crypto.Util.getRandomBigIntegerOfNbits(a); if (b.compareTo(c) != -1) { return c } } }; KJUR.crypto.Util.getRandomBigIntegerMinToMax = function (e, b) { var c = e.compareTo(b); if (c == 1) { throw \"biMin is greater than biMax\" } if (c == 0) { return e } var a = b.subtract(e); var d = KJUR.crypto.Util.getRandomBigIntegerZeroToMax(a); return d.add(e) }; KJUR.crypto.MessageDigest = function (c) { var b = null; var a = null; var d = null; this.setAlgAndProvider = function (g, f) { g = KJUR.crypto.MessageDigest.getCanonicalAlgName(g); if (g !== null && f === undefined) { f = KJUR.crypto.Util.DEFAULTPROVIDER[g] } if (\":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:\".indexOf(g) != -1 && f == \"cryptojs\") { try { this.md = KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[g].create() } catch (e) { throw \"setAlgAndProvider hash alg set fail alg=\" + g + \"/\" + e } this.updateString = function (h) { this.md.update(h) }; this.updateHex = function (h) { var i = CryptoJS.enc.Hex.parse(h); this.md.update(i) }; this.digest = function () { var h = this.md.finalize(); return h.toString(CryptoJS.enc.Hex) }; this.digestString = function (h) { this.updateString(h); return this.digest() }; this.digestHex = function (h) { this.updateHex(h); return this.digest() } } if (\":sha256:\".indexOf(g) != -1 && f == \"sjcl\") { try { this.md = new sjcl.hash.sha256() } catch (e) { throw \"setAlgAndProvider hash alg set fail alg=\" + g + \"/\" + e } this.updateString = function (h) { this.md.update(h) }; this.updateHex = function (i) { var h = sjcl.codec.hex.toBits(i); this.md.update(h) }; this.digest = function () { var h = this.md.finalize(); return sjcl.codec.hex.fromBits(h) }; this.digestString = function (h) { this.updateString(h); return this.digest() }; this.digestHex = function (h) { this.updateHex(h); return this.digest() } } }; this.updateString = function (e) { throw \"updateString(str) not supported for this alg/prov: \" + this.algName + \"/\" + this.provName }; this.updateHex = function (e) { throw \"updateHex(hex) not supported for this alg/prov: \" + this.algName + \"/\" + this.provName }; this.digest = function () { throw \"digest() not supported for this alg/prov: \" + this.algName + \"/\" + this.provName }; this.digestString = function (e) { throw \"digestString(str) not supported for this alg/prov: \" + this.algName + \"/\" + this.provName }; this.digestHex = function (e) { throw \"digestHex(hex) not supported for this alg/prov: \" + this.algName + \"/\" + this.provName }; if (c !== undefined) { if (c.alg !== undefined) { this.algName = c.alg; if (c.prov === undefined) { this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName] } this.setAlgAndProvider(this.algName, this.provName) } } }; KJUR.crypto.MessageDigest.getCanonicalAlgName = function (a) { if (typeof a === \"string\") { a = a.toLowerCase(); a = a.replace(/-/, \"\") } return a }; KJUR.crypto.MessageDigest.getHashLength = function (c) { var b = KJUR.crypto.MessageDigest; var a = b.getCanonicalAlgName(c); if (b.HASHLENGTH[a] === undefined) { throw \"not supported algorithm: \" + c } return b.HASHLENGTH[a] }; KJUR.crypto.MessageDigest.HASHLENGTH = { md5: 16, sha1: 20, sha224: 28, sha256: 32, sha384: 48, sha512: 64, ripemd160: 20 }; KJUR.crypto.Mac = function (d) { var f = null; var c = null; var a = null; var e = null; var b = null; this.setAlgAndProvider = function (k, i) { k = k.toLowerCase(); if (k == null) { k = \"hmacsha1\" } k = k.toLowerCase(); if (k.substr(0, 4) != \"hmac\") { throw \"setAlgAndProvider unsupported HMAC alg: \" + k } if (i === undefined) { i = KJUR.crypto.Util.DEFAULTPROVIDER[k] } this.algProv = k + \"/\" + i; var g = k.substr(4); if (\":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:\".indexOf(g) != -1 && i == \"cryptojs\") { try { var j = KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[g]; this.mac = CryptoJS.algo.HMAC.create(j, this.pass) } catch (h) { throw \"setAlgAndProvider hash alg set fail hashAlg=\" + g + \"/\" + h } this.updateString = function (l) { this.mac.update(l) }; this.updateHex = function (l) { var m = CryptoJS.enc.Hex.parse(l); this.mac.update(m) }; this.doFinal = function () { var l = this.mac.finalize(); return l.toString(CryptoJS.enc.Hex) }; this.doFinalString = function (l) { this.updateString(l); return this.doFinal() }; this.doFinalHex = function (l) { this.updateHex(l); return this.doFinal() } } }; this.updateString = function (g) { throw \"updateString(str) not supported for this alg/prov: \" + this.algProv }; this.updateHex = function (g) { throw \"updateHex(hex) not supported for this alg/prov: \" + this.algProv }; this.doFinal = function () { throw \"digest() not supported for this alg/prov: \" + this.algProv }; this.doFinalString = function (g) { throw \"digestString(str) not supported for this alg/prov: \" + this.algProv }; this.doFinalHex = function (g) { throw \"digestHex(hex) not supported for this alg/prov: \" + this.algProv }; this.setPassword = function (h) { if (typeof h == \"string\") { var g = h; if (h.length % 2 == 1 || !h.match(/^[0-9A-Fa-f]+$/)) { g = rstrtohex(h) } this.pass = CryptoJS.enc.Hex.parse(g); return } if (typeof h != \"object\") { throw \"KJUR.crypto.Mac unsupported password type: \" + h } var g = null; if (h.hex !== undefined) { if (h.hex.length % 2 != 0 || !h.hex.match(/^[0-9A-Fa-f]+$/)) { throw \"Mac: wrong hex password: \" + h.hex } g = h.hex } if (h.utf8 !== undefined) { g = utf8tohex(h.utf8) } if (h.rstr !== undefined) { g = rstrtohex(h.rstr) } if (h.b64 !== undefined) { g = b64tohex(h.b64) } if (h.b64u !== undefined) { g = b64utohex(h.b64u) } if (g == null) { throw \"KJUR.crypto.Mac unsupported password type: \" + h } this.pass = CryptoJS.enc.Hex.parse(g) }; if (d !== undefined) { if (d.pass !== undefined) { this.setPassword(d.pass) } if (d.alg !== undefined) { this.algName = d.alg; if (d.prov === undefined) { this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName] } this.setAlgAndProvider(this.algName, this.provName) } } }; KJUR.crypto.Signature = function (o) { var q = null; var n = null; var r = null; var c = null; var l = null; var d = null; var k = null; var h = null; var p = null; var e = null; var b = -1; var g = null; var j = null; var a = null; var i = null; var f = null; this._setAlgNames = function () { var s = this.algName.match(/^(.+)with(.+)$/); if (s) { this.mdAlgName = s[1].toLowerCase(); this.pubkeyAlgName = s[2].toLowerCase() } }; this._zeroPaddingOfSignature = function (x, w) { var v = \"\"; var t = w / 4 - x.length; for (var u = 0; u < t; u++) { v = v + \"0\" } return v + x }; this.setAlgAndProvider = function (u, t) { this._setAlgNames(); if (t != \"cryptojs/jsrsa\") { throw \"provider not supported: \" + t } if (\":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:\".indexOf(this.mdAlgName) != -1) { try { this.md = new KJUR.crypto.MessageDigest({ alg: this.mdAlgName }) } catch (s) { throw \"setAlgAndProvider hash alg set fail alg=\" + this.mdAlgName + \"/\" + s } this.init = function (w, x) { var y = null; try { if (x === undefined) { y = KEYUTIL.getKey(w) } else { y = KEYUTIL.getKey(w, x) } } catch (v) { throw \"init failed:\" + v } if (y.isPrivate === true) { this.prvKey = y; this.state = \"SIGN\" } else { if (y.isPublic === true) { this.pubKey = y; this.state = \"VERIFY\" } else { throw \"init failed.:\" + y } } }; this.updateString = function (v) { this.md.updateString(v) }; this.updateHex = function (v) { this.md.updateHex(v) }; this.sign = function () { this.sHashHex = this.md.digest(); if (typeof this.ecprvhex != \"undefined\" && typeof this.eccurvename != \"undefined\") { var v = new KJUR.crypto.ECDSA({ curve: this.eccurvename }); this.hSign = v.signHex(this.sHashHex, this.ecprvhex) } else { if (this.prvKey instanceof RSAKey && this.pubkeyAlgName === \"rsaandmgf1\") { this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex, this.mdAlgName, this.pssSaltLen) } else { if (this.prvKey instanceof RSAKey && this.pubkeyAlgName === \"rsa\") { this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, this.mdAlgName) } else { if (this.prvKey instanceof KJUR.crypto.ECDSA) { this.hSign = this.prvKey.signWithMessageHash(this.sHashHex) } else { if (this.prvKey instanceof KJUR.crypto.DSA) { this.hSign = this.prvKey.signWithMessageHash(this.sHashHex) } else { throw \"Signature: unsupported private key alg: \" + this.pubkeyAlgName } } } } } return this.hSign }; this.signString = function (v) { this.updateString(v); return this.sign() }; this.signHex = function (v) { this.updateHex(v); return this.sign() }; this.verify = function (v) { this.sHashHex = this.md.digest(); if (typeof this.ecpubhex != \"undefined\" && typeof this.eccurvename != \"undefined\") { var w = new KJUR.crypto.ECDSA({ curve: this.eccurvename }); return w.verifyHex(this.sHashHex, v, this.ecpubhex) } else { if (this.pubKey instanceof RSAKey && this.pubkeyAlgName === \"rsaandmgf1\") { return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, v, this.mdAlgName, this.pssSaltLen) } else { if (this.pubKey instanceof RSAKey && this.pubkeyAlgName === \"rsa\") { return this.pubKey.verifyWithMessageHash(this.sHashHex, v) } else { if (KJUR.crypto.ECDSA !== undefined && this.pubKey instanceof KJUR.crypto.ECDSA) { return this.pubKey.verifyWithMessageHash(this.sHashHex, v) } else { if (KJUR.crypto.DSA !== undefined && this.pubKey instanceof KJUR.crypto.DSA) { return this.pubKey.verifyWithMessageHash(this.sHashHex, v) } else { throw \"Signature: unsupported public key alg: \" + this.pubkeyAlgName } } } } } } } }; this.init = function (s, t) { throw \"init(key, pass) not supported for this alg:prov=\" + this.algProvName }; this.updateString = function (s) { throw \"updateString(str) not supported for this alg:prov=\" + this.algProvName }; this.updateHex = function (s) { throw \"updateHex(hex) not supported for this alg:prov=\" + this.algProvName }; this.sign = function () { throw \"sign() not supported for this alg:prov=\" + this.algProvName }; this.signString = function (s) { throw \"digestString(str) not supported for this alg:prov=\" + this.algProvName }; this.signHex = function (s) { throw \"digestHex(hex) not supported for this alg:prov=\" + this.algProvName }; this.verify = function (s) { throw \"verify(hSigVal) not supported for this alg:prov=\" + this.algProvName }; this.initParams = o; if (o !== undefined) { if (o.alg !== undefined) { this.algName = o.alg; if (o.prov === undefined) { this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName] } else { this.provName = o.prov } this.algProvName = this.algName + \":\" + this.provName; this.setAlgAndProvider(this.algName, this.provName); this._setAlgNames() } if (o.psssaltlen !== undefined) { this.pssSaltLen = o.psssaltlen } if (o.prvkeypem !== undefined) { if (o.prvkeypas !== undefined) { throw \"both prvkeypem and prvkeypas parameters not supported\" } else { try { var q = KEYUTIL.getKey(o.prvkeypem); this.init(q) } catch (m) { throw \"fatal error to load pem private key: \" + m } } } } }; KJUR.crypto.Cipher = function (a) { }; KJUR.crypto.Cipher.encrypt = function (e, f, d) { if (f instanceof RSAKey && f.isPublic) { var c = KJUR.crypto.Cipher.getAlgByKeyAndName(f, d); if (c === \"RSA\") { return f.encrypt(e) } if (c === \"RSAOAEP\") { return f.encryptOAEP(e, \"sha1\") } var b = c.match(/^RSAOAEP(\\d+)$/); if (b !== null) { return f.encryptOAEP(e, \"sha\" + b[1]) } throw \"Cipher.encrypt: unsupported algorithm for RSAKey: \" + d } else { throw \"Cipher.encrypt: unsupported key or algorithm\" } }; KJUR.crypto.Cipher.decrypt = function (e, f, d) { if (f instanceof RSAKey && f.isPrivate) { var c = KJUR.crypto.Cipher.getAlgByKeyAndName(f, d); if (c === \"RSA\") { return f.decrypt(e) } if (c === \"RSAOAEP\") { return f.decryptOAEP(e, \"sha1\") } var b = c.match(/^RSAOAEP(\\d+)$/); if (b !== null) { return f.decryptOAEP(e, \"sha\" + b[1]) } throw \"Cipher.decrypt: unsupported algorithm for RSAKey: \" + d } else { throw \"Cipher.decrypt: unsupported key or algorithm\" } }; KJUR.crypto.Cipher.getAlgByKeyAndName = function (b, a) { if (b instanceof RSAKey) { if (\":RSA:RSAOAEP:RSAOAEP224:RSAOAEP256:RSAOAEP384:RSAOAEP512:\".indexOf(a) != -1) { return a } if (a === null || a === undefined) { return \"RSA\" } throw \"getAlgByKeyAndName: not supported algorithm name for RSAKey: \" + a } throw \"getAlgByKeyAndName: not supported algorithm name: \" + a }; KJUR.crypto.OID = new function () { this.oidhex2name = { \"2a864886f70d010101\": \"rsaEncryption\", \"2a8648ce3d0201\": \"ecPublicKey\", \"2a8648ce380401\": \"dsa\", \"2a8648ce3d030107\": \"secp256r1\", \"2b8104001f\": \"secp192k1\", \"2b81040021\": \"secp224r1\", \"2b8104000a\": \"secp256k1\", \"2b81040023\": \"secp521r1\", \"2b81040022\": \"secp384r1\", \"2a8648ce380403\": \"SHA1withDSA\", \"608648016503040301\": \"SHA224withDSA\", \"608648016503040302\": \"SHA256withDSA\", } };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.crypto == \"undefined\" || !KJUR.crypto) { KJUR.crypto = {} } KJUR.crypto.ECDSA = function (h) { var e = \"secp256r1\"; var g = null; var b = null; var f = null; var a = new SecureRandom(); var d = null; this.type = \"EC\"; this.isPrivate = false; this.isPublic = false; function c(s, o, r, n) { var j = Math.max(o.bitLength(), n.bitLength()); var t = s.add2D(r); var q = s.curve.getInfinity(); for (var p = j - 1; p >= 0; --p) { q = q.twice2D(); q.z = BigInteger.ONE; if (o.testBit(p)) { if (n.testBit(p)) { q = q.add2D(t) } else { q = q.add2D(s) } } else { if (n.testBit(p)) { q = q.add2D(r) } } } return q } this.getBigRandom = function (i) { return new BigInteger(i.bitLength(), a).mod(i.subtract(BigInteger.ONE)).add(BigInteger.ONE) }; this.setNamedCurve = function (i) { this.ecparams = KJUR.crypto.ECParameterDB.getByName(i); this.prvKeyHex = null; this.pubKeyHex = null; this.curveName = i }; this.setPrivateKeyHex = function (i) { this.isPrivate = true; this.prvKeyHex = i }; this.setPublicKeyHex = function (i) { this.isPublic = true; this.pubKeyHex = i }; this.getPublicKeyXYHex = function () { var k = this.pubKeyHex; if (k.substr(0, 2) !== \"04\") { throw \"this method supports uncompressed format(04) only\" } var j = this.ecparams.keylen / 4; if (k.length !== 2 + j * 2) { throw \"malformed public key hex length\" } var i = {}; i.x = k.substr(2, j); i.y = k.substr(2 + j); return i }; this.getShortNISTPCurveName = function () { var i = this.curveName; if (i === \"secp256r1\" || i === \"NIST P-256\" || i === \"P-256\" || i === \"prime256v1\") { return \"P-256\" } if (i === \"secp384r1\" || i === \"NIST P-384\" || i === \"P-384\") { return \"P-384\" } return null }; this.generateKeyPairHex = function () { var k = this.ecparams.n; var n = this.getBigRandom(k); var l = this.ecparams.G.multiply(n); var q = l.getX().toBigInteger(); var o = l.getY().toBigInteger(); var i = this.ecparams.keylen / 4; var m = (\"0000000000\" + n.toString(16)).slice(-i); var r = (\"0000000000\" + q.toString(16)).slice(-i); var p = (\"0000000000\" + o.toString(16)).slice(-i); var j = \"04\" + r + p; this.setPrivateKeyHex(m); this.setPublicKeyHex(j); return { ecprvhex: m, ecpubhex: j } }; this.signWithMessageHash = function (i) { return this.signHex(i, this.prvKeyHex) }; this.signHex = function (o, j) { var t = new BigInteger(j, 16); var l = this.ecparams.n; var q = new BigInteger(o, 16); do { var m = this.getBigRandom(l); var u = this.ecparams.G; var p = u.multiply(m); var i = p.getX().toBigInteger().mod(l) } while (i.compareTo(BigInteger.ZERO) <= 0); var v = m.modInverse(l).multiply(q.add(t.multiply(i))).mod(l); return KJUR.crypto.ECDSA.biRSSigToASN1Sig(i, v) }; this.sign = function (m, u) { var q = u; var j = this.ecparams.n; var p = BigInteger.fromByteArrayUnsigned(m); do { var l = this.getBigRandom(j); var t = this.ecparams.G; var o = t.multiply(l); var i = o.getX().toBigInteger().mod(j) } while (i.compareTo(BigInteger.ZERO) <= 0); var v = l.modInverse(j).multiply(p.add(q.multiply(i))).mod(j); return this.serializeSig(i, v) }; this.verifyWithMessageHash = function (j, i) { return this.verifyHex(j, i, this.pubKeyHex) }; this.verifyHex = function (m, i, p) { var l, j; var o = KJUR.crypto.ECDSA.parseSigHex(i); l = o.r; j = o.s; var k; k = ECPointFp.decodeFromHex(this.ecparams.curve, p); var n = new BigInteger(m, 16); return this.verifyRaw(n, l, j, k) }; this.verify = function (o, p, j) { var l, i; if (Bitcoin.Util.isArray(p)) { var n = this.parseSig(p); l = n.r; i = n.s } else { if (\"object\" === typeof p && p.r && p.s) { l = p.r; i = p.s } else { throw \"Invalid value for signature\" } } var k; if (j instanceof ECPointFp) { k = j } else { if (Bitcoin.Util.isArray(j)) { k = ECPointFp.decodeFrom(this.ecparams.curve, j) } else { throw \"Invalid format for pubkey value, must be byte array or ECPointFp\" } } var m = BigInteger.fromByteArrayUnsigned(o); return this.verifyRaw(m, l, i, k) }; this.verifyRaw = function (o, i, w, m) { var l = this.ecparams.n; var u = this.ecparams.G; if (i.compareTo(BigInteger.ONE) < 0 || i.compareTo(l) >= 0) { return false } if (w.compareTo(BigInteger.ONE) < 0 || w.compareTo(l) >= 0) { return false } var p = w.modInverse(l); var k = o.multiply(p).mod(l); var j = i.multiply(p).mod(l); var q = u.multiply(k).add(m.multiply(j)); var t = q.getX().toBigInteger().mod(l); return t.equals(i) }; this.serializeSig = function (k, j) { var l = k.toByteArraySigned(); var i = j.toByteArraySigned(); var m = []; m.push(2); m.push(l.length); m = m.concat(l); m.push(2); m.push(i.length); m = m.concat(i); m.unshift(m.length); m.unshift(48); return m }; this.parseSig = function (n) { var m; if (n[0] != 48) { throw new Error(\"Signature not a valid DERSequence\") } m = 2; if (n[m] != 2) { throw new Error(\"First element in signature must be a DERInteger\") } var l = n.slice(m + 2, m + 2 + n[m + 1]); m += 2 + n[m + 1]; if (n[m] != 2) { throw new Error(\"Second element in signature must be a DERInteger\") } var i = n.slice(m + 2, m + 2 + n[m + 1]); m += 2 + n[m + 1]; var k = BigInteger.fromByteArrayUnsigned(l); var j = BigInteger.fromByteArrayUnsigned(i); return { r: k, s: j } }; this.parseSigCompact = function (m) { if (m.length !== 65) { throw \"Signature has the wrong length\" } var j = m[0] - 27; if (j < 0 || j > 7) { throw \"Invalid signature type\" } var o = this.ecparams.n; var l = BigInteger.fromByteArrayUnsigned(m.slice(1, 33)).mod(o); var k = BigInteger.fromByteArrayUnsigned(m.slice(33, 65)).mod(o); return { r: l, s: k, i: j } }; this.readPKCS5PrvKeyHex = function (l) { var n = ASN1HEX; var m = KJUR.crypto.ECDSA.getName; var p = n.getVbyList; if (n.isASN1HEX(l) === false) { throw \"not ASN.1 hex string\" } var i, k, o; try { i = p(l, 0, [2, 0], \"06\"); k = p(l, 0, [1], \"04\"); try { o = p(l, 0, [3, 0], \"03\").substr(2) } catch (j) { } } catch (j) { throw \"malformed PKCS#1/5 plain ECC private key\" } this.curveName = m(i); if (this.curveName === undefined) { throw \"unsupported curve name\" } this.setNamedCurve(this.curveName); this.setPublicKeyHex(o); this.setPrivateKeyHex(k); this.isPublic = false }; this.readPKCS8PrvKeyHex = function (l) { var q = ASN1HEX; var i = KJUR.crypto.ECDSA.getName; var n = q.getVbyList; if (q.isASN1HEX(l) === false) { throw \"not ASN.1 hex string\" } var j, p, m, k; try { j = n(l, 0, [1, 0], \"06\"); p = n(l, 0, [1, 1], \"06\"); m = n(l, 0, [2, 0, 1], \"04\"); try { k = n(l, 0, [2, 0, 2, 0], \"03\").substr(2) } catch (o) { } } catch (o) { throw \"malformed PKCS#8 plain ECC private key\" } this.curveName = i(p); if (this.curveName === undefined) { throw \"unsupported curve name\" } this.setNamedCurve(this.curveName); this.setPublicKeyHex(k); this.setPrivateKeyHex(m); this.isPublic = false }; this.readPKCS8PubKeyHex = function (l) { var n = ASN1HEX; var m = KJUR.crypto.ECDSA.getName; var p = n.getVbyList; if (n.isASN1HEX(l) === false) { throw \"not ASN.1 hex string\" } var k, i, o; try { k = p(l, 0, [0, 0], \"06\"); i = p(l, 0, [0, 1], \"06\"); o = p(l, 0, [1], \"03\").substr(2) } catch (j) { throw \"malformed PKCS#8 ECC public key\" } this.curveName = m(i); if (this.curveName === null) { throw \"unsupported curve name\" } this.setNamedCurve(this.curveName); this.setPublicKeyHex(o) }; this.readCertPubKeyHex = function (k, p) { if (p !== 5) { p = 6 } var m = ASN1HEX; var l = KJUR.crypto.ECDSA.getName; var o = m.getVbyList; if (m.isASN1HEX(k) === false) { throw \"not ASN.1 hex string\" } var i, n; try { i = o(k, 0, [0, p, 0, 1], \"06\"); n = o(k, 0, [0, p, 1], \"03\").substr(2) } catch (j) { throw \"malformed X.509 certificate ECC public key\" } this.curveName = l(i); if (this.curveName === null) { throw \"unsupported curve name\" } this.setNamedCurve(this.curveName); this.setPublicKeyHex(n) }; if (h !== undefined) { if (h.curve !== undefined) { this.curveName = h.curve } } if (this.curveName === undefined) { this.curveName = e } this.setNamedCurve(this.curveName); if (h !== undefined) { if (h.prv !== undefined) { this.setPrivateKeyHex(h.prv) } if (h.pub !== undefined) { this.setPublicKeyHex(h.pub) } } }; KJUR.crypto.ECDSA.parseSigHex = function (a) { var b = KJUR.crypto.ECDSA.parseSigHexInHexRS(a); var d = new BigInteger(b.r, 16); var c = new BigInteger(b.s, 16); return { r: d, s: c } }; KJUR.crypto.ECDSA.parseSigHexInHexRS = function (f) { var j = ASN1HEX; var i = j.getChildIdx; var g = j.getV; if (f.substr(0, 2) != \"30\") { throw \"signature is not a ASN.1 sequence\" } var h = i(f, 0); if (h.length != 2) { throw \"number of signature ASN.1 sequence elements seem wrong\" } var e = h[0]; var d = h[1]; if (f.substr(e, 2) != \"02\") { throw \"1st item of sequene of signature is not ASN.1 integer\" } if (f.substr(d, 2) != \"02\") { throw \"2nd item of sequene of signature is not ASN.1 integer\" } var c = g(f, e); var b = g(f, d); return { r: c, s: b } }; KJUR.crypto.ECDSA.asn1SigToConcatSig = function (c) { var d = KJUR.crypto.ECDSA.parseSigHexInHexRS(c); var b = d.r; var a = d.s; if (b.substr(0, 2) == \"00\" && (((b.length / 2) * 8) % (16 * 8)) == 8) { b = b.substr(2) } if (a.substr(0, 2) == \"00\" && (((a.length / 2) * 8) % (16 * 8)) == 8) { a = a.substr(2) } if ((((b.length / 2) * 8) % (16 * 8)) != 0) { throw \"unknown ECDSA sig r length error\" } if ((((a.length / 2) * 8) % (16 * 8)) != 0) { throw \"unknown ECDSA sig s length error\" } return b + a }; KJUR.crypto.ECDSA.concatSigToASN1Sig = function (a) { if ((((a.length / 2) * 8) % (16 * 8)) != 0) { throw \"unknown ECDSA concatinated r-s sig  length error\" } var c = a.substr(0, a.length / 2); var b = a.substr(a.length / 2); return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(c, b) }; KJUR.crypto.ECDSA.hexRSSigToASN1Sig = function (b, a) { var d = new BigInteger(b, 16); var c = new BigInteger(a, 16); return KJUR.crypto.ECDSA.biRSSigToASN1Sig(d, c) }; KJUR.crypto.ECDSA.biRSSigToASN1Sig = function (f, d) { var c = KJUR.asn1; var b = new c.DERInteger({ bigint: f }); var a = new c.DERInteger({ bigint: d }); var e = new c.DERSequence({ array: [b, a] }); return e.getEncodedHex() }; KJUR.crypto.ECDSA.getName = function (a) { if (a === \"2a8648ce3d030107\") { return \"secp256r1\" } if (a === \"2b8104000a\") { return \"secp256k1\" } if (a === \"2b81040022\") { return \"secp384r1\" } if (\"|secp256r1|NIST P-256|P-256|prime256v1|\".indexOf(a) !== -1) { return \"secp256r1\" } if (\"|secp256k1|\".indexOf(a) !== -1) { return \"secp256k1\" } if (\"|secp384r1|NIST P-384|P-384|\".indexOf(a) !== -1) { return \"secp384r1\" } return null };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.crypto == \"undefined\" || !KJUR.crypto) { KJUR.crypto = {} } KJUR.crypto.ECParameterDB = new function () { var b = {}; var c = {}; function a(d) { return new BigInteger(d, 16) } this.getByName = function (e) { var d = e; if (typeof c[d] != \"undefined\") { d = c[e] } if (typeof b[d] != \"undefined\") { return b[d] } throw \"unregistered EC curve name: \" + d }; this.regist = function (A, l, o, g, m, e, j, f, k, u, d, x) { b[A] = {}; var s = a(o); var z = a(g); var y = a(m); var t = a(e); var w = a(j); var r = new ECCurveFp(s, z, y); var q = r.decodePointHex(\"04\" + f + k); b[A][\"name\"] = A; b[A][\"keylen\"] = l; b[A][\"curve\"] = r; b[A][\"G\"] = q; b[A][\"n\"] = t; b[A][\"h\"] = w; b[A][\"oid\"] = d; b[A][\"info\"] = x; for (var v = 0; v < u.length; v++) { c[u[v]] = A } } }; KJUR.crypto.ECParameterDB.regist(\"secp128r1\", 128, \"FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF\", \"FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC\", \"E87579C11079F43DD824993C2CEE5ED3\", \"FFFFFFFE0000000075A30D1B9038A115\", \"1\", \"161FF7528B899B2D0C28607CA52C5B86\", \"CF5AC8395BAFEB13C02DA292DDED7A83\", [], \"\", \"secp128r1 : SECG curve over a 128 bit prime field\"); KJUR.crypto.ECParameterDB.regist(\"secp160k1\", 160, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73\", \"0\", \"7\", \"0100000000000000000001B8FA16DFAB9ACA16B6B3\", \"1\", \"3B4C382CE37AA192A4019E763036F4F5DD4D7EBB\", \"938CF935318FDCED6BC28286531733C3F03C4FEE\", [], \"\", \"secp160k1 : SECG curve over a 160 bit prime field\"); KJUR.crypto.ECParameterDB.regist(\"secp160r1\", 160, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC\", \"1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45\", \"0100000000000000000001F4C8F927AED3CA752257\", \"1\", \"4A96B5688EF573284664698968C38BB913CBFC82\", \"23A628553168947D59DCC912042351377AC5FB32\", [], \"\", \"secp160r1 : SECG curve over a 160 bit prime field\"); KJUR.crypto.ECParameterDB.regist(\"secp192k1\", 192, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37\", \"0\", \"3\", \"FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D\", \"1\", \"DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D\", \"9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D\", []); KJUR.crypto.ECParameterDB.regist(\"secp192r1\", 192, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC\", \"64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1\", \"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831\", \"1\", \"188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012\", \"07192B95FFC8DA78631011ED6B24CDD573F977A11E794811\", []); KJUR.crypto.ECParameterDB.regist(\"secp224r1\", 224, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE\", \"B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D\", \"1\", \"B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21\", \"BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34\", []); KJUR.crypto.ECParameterDB.regist(\"secp256k1\", 256, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F\", \"0\", \"7\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141\", \"1\", \"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798\", \"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8\", []); KJUR.crypto.ECParameterDB.regist(\"secp256r1\", 256, \"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF\", \"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC\", \"5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B\", \"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551\", \"1\", \"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296\", \"4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5\", [\"NIST P-256\", \"P-256\", \"prime256v1\"]); KJUR.crypto.ECParameterDB.regist(\"secp384r1\", 384, \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC\", \"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF\", \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973\", \"1\", \"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7\", \"3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f\", [\"NIST P-384\", \"P-384\"]); KJUR.crypto.ECParameterDB.regist(\"secp521r1\", 521, \"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\", \"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC\", \"051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00\", \"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409\", \"1\", \"C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66\", \"011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650\", [\"NIST P-521\", \"P-521\"]);\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.crypto == \"undefined\" || !KJUR.crypto) { KJUR.crypto = {} } KJUR.crypto.DSA = function () { this.p = null; this.q = null; this.g = null; this.y = null; this.x = null; this.type = \"DSA\"; this.isPrivate = false; this.isPublic = false; this.setPrivate = function (d, c, b, e, a) { this.isPrivate = true; this.p = d; this.q = c; this.g = b; this.y = e; this.x = a }; this.setPrivateHex = function (d, b, f, i, j) { var c, a, e, g, h; c = new BigInteger(d, 16); a = new BigInteger(b, 16); e = new BigInteger(f, 16); if (typeof i === \"string\" && i.length > 1) { g = new BigInteger(i, 16) } else { g = null } h = new BigInteger(j, 16); this.setPrivate(c, a, e, g, h) }; this.setPublic = function (c, b, a, d) { this.isPublic = true; this.p = c; this.q = b; this.g = a; this.y = d; this.x = null }; this.setPublicHex = function (f, e, d, g) { var b, a, h, c; b = new BigInteger(f, 16); a = new BigInteger(e, 16); h = new BigInteger(d, 16); c = new BigInteger(g, 16); this.setPublic(b, a, h, c) }; this.signWithMessageHash = function (d) { var c = this.p; var b = this.q; var f = this.g; var i = this.y; var j = this.x; var e = KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE), b.subtract(BigInteger.ONE)); var l = d.substr(0, b.bitLength() / 4); var h = new BigInteger(l, 16); var a = (f.modPow(e, c)).mod(b); var n = (e.modInverse(b).multiply(h.add(j.multiply(a)))).mod(b); var m = KJUR.asn1.ASN1Util.jsonToASN1HEX({ seq: [{ \"int\": { bigint: a } }, { \"int\": { bigint: n } }] }); return m }; this.verifyWithMessageHash = function (h, f) { var d = this.p; var b = this.q; var j = this.g; var l = this.y; var i = this.parseASN1Signature(f); var a = i[0]; var t = i[1]; var o = h.substr(0, b.bitLength() / 4); var k = new BigInteger(o, 16); if (BigInteger.ZERO.compareTo(a) > 0 || a.compareTo(b) > 0) { throw \"invalid DSA signature\" } if (BigInteger.ZERO.compareTo(t) >= 0 || t.compareTo(b) > 0) { throw \"invalid DSA signature\" } var m = t.modInverse(b); var e = k.multiply(m).mod(b); var c = a.multiply(m).mod(b); var n = j.modPow(e, d).multiply(l.modPow(c, d)).mod(d).mod(b); return n.compareTo(a) == 0 }; this.parseASN1Signature = function (a) { try { var d = new BigInteger(ASN1HEX.getVbyList(a, 0, [0], \"02\"), 16); var c = new BigInteger(ASN1HEX.getVbyList(a, 0, [1], \"02\"), 16); return [d, c] } catch (b) { throw \"malformed ASN.1 DSA signature\" } }; this.readPKCS5PrvKeyHex = function (c) { var b, a, f, g, i; var j = ASN1HEX; var d = j.getVbyList; if (j.isASN1HEX(c) === false) { throw \"not ASN.1 hex string\" } try { b = d(c, 0, [1], \"02\"); a = d(c, 0, [2], \"02\"); f = d(c, 0, [3], \"02\"); g = d(c, 0, [4], \"02\"); i = d(c, 0, [5], \"02\") } catch (e) { console.log(\"EXCEPTION:\" + e); throw \"malformed PKCS#1/5 plain DSA private key\" } this.setPrivateHex(b, a, f, g, i) }; this.readPKCS8PrvKeyHex = function (d) { var f, c, b, g; var e = ASN1HEX; var i = e.getVbyList; if (e.isASN1HEX(d) === false) { throw \"not ASN.1 hex string\" } try { f = i(d, 0, [1, 1, 0], \"02\"); c = i(d, 0, [1, 1, 1], \"02\"); b = i(d, 0, [1, 1, 2], \"02\"); g = i(d, 0, [2, 0], \"02\") } catch (a) { console.log(\"EXCEPTION:\" + a); throw \"malformed PKCS#8 plain DSA private key\" } this.setPrivateHex(f, c, b, null, g) }; this.readPKCS8PubKeyHex = function (d) { var f, c, b, g; var e = ASN1HEX; var i = e.getVbyList; if (e.isASN1HEX(d) === false) { throw \"not ASN.1 hex string\" } try { f = i(d, 0, [0, 1, 0], \"02\"); c = i(d, 0, [0, 1, 1], \"02\"); b = i(d, 0, [0, 1, 2], \"02\"); g = i(d, 0, [1, 0], \"02\") } catch (a) { console.log(\"EXCEPTION:\" + a); throw \"malformed PKCS#8 DSA public key\" } this.setPublicHex(f, c, b, g) }; this.readCertPubKeyHex = function (c, f) { if (f !== 5) { f = 6 } var b, a, g, i; var j = ASN1HEX; var d = j.getVbyList; if (j.isASN1HEX(c) === false) { throw \"not ASN.1 hex string\" } try { b = d(c, 0, [0, f, 0, 1, 0], \"02\"); a = d(c, 0, [0, f, 0, 1, 1], \"02\"); g = d(c, 0, [0, f, 0, 1, 2], \"02\"); i = d(c, 0, [0, f, 1, 0], \"02\") } catch (e) { console.log(\"EXCEPTION:\" + e); throw \"malformed X.509 certificate DSA public key\" } this.setPublicHex(b, a, g, i) } };\nvar KEYUTIL = function () { var d = function (p, r, q) { return k(CryptoJS.AES, p, r, q) }; var e = function (p, r, q) { return k(CryptoJS.TripleDES, p, r, q) }; var a = function (p, r, q) { return k(CryptoJS.DES, p, r, q) }; var k = function (s, x, u, q) { var r = CryptoJS.enc.Hex.parse(x); var w = CryptoJS.enc.Hex.parse(u); var p = CryptoJS.enc.Hex.parse(q); var t = {}; t.key = w; t.iv = p; t.ciphertext = r; var v = s.decrypt(t, w, { iv: p }); return CryptoJS.enc.Hex.stringify(v) }; var l = function (p, r, q) { return g(CryptoJS.AES, p, r, q) }; var o = function (p, r, q) { return g(CryptoJS.TripleDES, p, r, q) }; var f = function (p, r, q) { return g(CryptoJS.DES, p, r, q) }; var g = function (t, y, v, q) { var s = CryptoJS.enc.Hex.parse(y); var x = CryptoJS.enc.Hex.parse(v); var p = CryptoJS.enc.Hex.parse(q); var w = t.encrypt(s, x, { iv: p }); var r = CryptoJS.enc.Hex.parse(w.toString()); var u = CryptoJS.enc.Base64.stringify(r); return u }; var i = { \"AES-256-CBC\": { proc: d, eproc: l, keylen: 32, ivlen: 16 }, \"AES-192-CBC\": { proc: d, eproc: l, keylen: 24, ivlen: 16 }, \"AES-128-CBC\": { proc: d, eproc: l, keylen: 16, ivlen: 16 }, \"DES-EDE3-CBC\": { proc: e, eproc: o, keylen: 24, ivlen: 8 }, \"DES-CBC\": { proc: a, eproc: f, keylen: 8, ivlen: 8 } }; var c = function (p) { return i[p][\"proc\"] }; var m = function (p) { var r = CryptoJS.lib.WordArray.random(p); var q = CryptoJS.enc.Hex.stringify(r); return q }; var n = function (v) { var w = {}; var q = v.match(new RegExp(\"DEK-Info: ([^,]+),([0-9A-Fa-f]+)\", \"m\")); if (q) { w.cipher = q[1]; w.ivsalt = q[2] } var p = v.match(new RegExp(\"-----BEGIN ([A-Z]+) PRIVATE KEY-----\")); if (p) { w.type = p[1] } var u = -1; var x = 0; if (v.indexOf(\"\\r\\n\\r\\n\") != -1) { u = v.indexOf(\"\\r\\n\\r\\n\"); x = 2 } if (v.indexOf(\"\\n\\n\") != -1) { u = v.indexOf(\"\\n\\n\"); x = 1 } var t = v.indexOf(\"-----END\"); if (u != -1 && t != -1) { var r = v.substring(u + x * 2, t - x); r = r.replace(/\\s+/g, \"\"); w.data = r } return w }; var j = function (q, y, p) { var v = p.substring(0, 16); var t = CryptoJS.enc.Hex.parse(v); var r = CryptoJS.enc.Utf8.parse(y); var u = i[q][\"keylen\"] + i[q][\"ivlen\"]; var x = \"\"; var w = null; for (; ;) { var s = CryptoJS.algo.MD5.create(); if (w != null) { s.update(w) } s.update(r); s.update(t); w = s.finalize(); x = x + CryptoJS.enc.Hex.stringify(w); if (x.length >= u * 2) { break } } var z = {}; z.keyhex = x.substr(0, i[q][\"keylen\"] * 2); z.ivhex = x.substr(i[q][\"keylen\"] * 2, i[q][\"ivlen\"] * 2); return z }; var b = function (p, v, r, w) { var s = CryptoJS.enc.Base64.parse(p); var q = CryptoJS.enc.Hex.stringify(s); var u = i[v][\"proc\"]; var t = u(q, r, w); return t }; var h = function (p, s, q, u) { var r = i[s][\"eproc\"]; var t = r(p, q, u); return t }; return { version: \"1.0.0\", parsePKCS5PEM: function (p) { return n(p) }, getKeyAndUnusedIvByPasscodeAndIvsalt: function (q, p, r) { return j(q, p, r) }, decryptKeyB64: function (p, r, q, s) { return b(p, r, q, s) }, getDecryptedKeyHex: function (y, x) { var q = n(y); var t = q.type; var r = q.cipher; var p = q.ivsalt; var s = q.data; var w = j(r, x, p); var v = w.keyhex; var u = b(s, r, v, p); return u }, getEncryptedPKCS5PEMFromPrvKeyHex: function (x, s, A, t, r) { var p = \"\"; if (typeof t == \"undefined\" || t == null) { t = \"AES-256-CBC\" } if (typeof i[t] == \"undefined\") { throw \"KEYUTIL unsupported algorithm: \" + t } if (typeof r == \"undefined\" || r == null) { var v = i[t][\"ivlen\"]; var u = m(v); r = u.toUpperCase() } var z = j(t, A, r); var y = z.keyhex; var w = h(s, t, y, r); var q = w.replace(/(.{64})/g, \"$1\\r\\n\"); var p = \"-----BEGIN \" + x + \" PRIVATE KEY-----\\r\\n\"; p += \"Proc-Type: 4,ENCRYPTED\\r\\n\"; p += \"DEK-Info: \" + t + \",\" + r + \"\\r\\n\"; p += \"\\r\\n\"; p += q; p += \"\\r\\n-----END \" + x + \" PRIVATE KEY-----\\r\\n\"; return p }, parseHexOfEncryptedPKCS8: function (y) { var B = ASN1HEX; var z = B.getChildIdx; var w = B.getV; var t = {}; var r = z(y, 0); if (r.length != 2) { throw \"malformed format: SEQUENCE(0).items != 2: \" + r.length } t.ciphertext = w(y, r[1]); var A = z(y, r[0]); if (A.length != 2) { throw \"malformed format: SEQUENCE(0.0).items != 2: \" + A.length } if (w(y, A[0]) != \"2a864886f70d01050d\") { throw \"this only supports pkcs5PBES2\" } var p = z(y, A[1]); if (A.length != 2) { throw \"malformed format: SEQUENCE(0.0.1).items != 2: \" + p.length } var q = z(y, p[1]); if (q.length != 2) { throw \"malformed format: SEQUENCE(0.0.1.1).items != 2: \" + q.length } if (w(y, q[0]) != \"2a864886f70d0307\") { throw \"this only supports TripleDES\" } t.encryptionSchemeAlg = \"TripleDES\"; t.encryptionSchemeIV = w(y, q[1]); var s = z(y, p[0]); if (s.length != 2) { throw \"malformed format: SEQUENCE(0.0.1.0).items != 2: \" + s.length } if (w(y, s[0]) != \"2a864886f70d01050c\") { throw \"this only supports pkcs5PBKDF2\" } var x = z(y, s[1]); if (x.length < 2) { throw \"malformed format: SEQUENCE(0.0.1.0.1).items < 2: \" + x.length } t.pbkdf2Salt = w(y, x[0]); var u = w(y, x[1]); try { t.pbkdf2Iter = parseInt(u, 16) } catch (v) { throw \"malformed format pbkdf2Iter: \" + u } return t }, getPBKDF2KeyHexFromParam: function (u, p) { var t = CryptoJS.enc.Hex.parse(u.pbkdf2Salt); var q = u.pbkdf2Iter; var s = CryptoJS.PBKDF2(p, t, { keySize: 192 / 32, iterations: q }); var r = CryptoJS.enc.Hex.stringify(s); return r }, _getPlainPKCS8HexFromEncryptedPKCS8PEM: function (x, y) { var r = pemtohex(x, \"ENCRYPTED PRIVATE KEY\"); var p = this.parseHexOfEncryptedPKCS8(r); var u = KEYUTIL.getPBKDF2KeyHexFromParam(p, y); var v = {}; v.ciphertext = CryptoJS.enc.Hex.parse(p.ciphertext); var t = CryptoJS.enc.Hex.parse(u); var s = CryptoJS.enc.Hex.parse(p.encryptionSchemeIV); var w = CryptoJS.TripleDES.decrypt(v, t, { iv: s }); var q = CryptoJS.enc.Hex.stringify(w); return q }, getKeyFromEncryptedPKCS8PEM: function (s, q) { var p = this._getPlainPKCS8HexFromEncryptedPKCS8PEM(s, q); var r = this.getKeyFromPlainPrivatePKCS8Hex(p); return r }, parsePlainPrivatePKCS8Hex: function (s) { var v = ASN1HEX; var u = v.getChildIdx; var t = v.getV; var q = {}; q.algparam = null; if (s.substr(0, 2) != \"30\") { throw \"malformed plain PKCS8 private key(code:001)\" } var r = u(s, 0); if (r.length != 3) { throw \"malformed plain PKCS8 private key(code:002)\" } if (s.substr(r[1], 2) != \"30\") { throw \"malformed PKCS8 private key(code:003)\" } var p = u(s, r[1]); if (p.length != 2) { throw \"malformed PKCS8 private key(code:004)\" } if (s.substr(p[0], 2) != \"06\") { throw \"malformed PKCS8 private key(code:005)\" } q.algoid = t(s, p[0]); if (s.substr(p[1], 2) == \"06\") { q.algparam = t(s, p[1]) } if (s.substr(r[2], 2) != \"04\") { throw \"malformed PKCS8 private key(code:006)\" } q.keyidx = v.getVidx(s, r[2]); return q }, getKeyFromPlainPrivatePKCS8PEM: function (q) { var p = pemtohex(q, \"PRIVATE KEY\"); var r = this.getKeyFromPlainPrivatePKCS8Hex(p); return r }, getKeyFromPlainPrivatePKCS8Hex: function (p) { var q = this.parsePlainPrivatePKCS8Hex(p); var r; if (q.algoid == \"2a864886f70d010101\") { r = new RSAKey() } else { if (q.algoid == \"2a8648ce380401\") { r = new KJUR.crypto.DSA() } else { if (q.algoid == \"2a8648ce3d0201\") { r = new KJUR.crypto.ECDSA() } else { throw \"unsupported private key algorithm\" } } } r.readPKCS8PrvKeyHex(p); return r }, _getKeyFromPublicPKCS8Hex: function (q) { var p; var r = ASN1HEX.getVbyList(q, 0, [0, 0], \"06\"); if (r === \"2a864886f70d010101\") { p = new RSAKey() } else { if (r === \"2a8648ce380401\") { p = new KJUR.crypto.DSA() } else { if (r === \"2a8648ce3d0201\") { p = new KJUR.crypto.ECDSA() } else { throw \"unsupported PKCS#8 public key hex\" } } } p.readPKCS8PubKeyHex(q); return p }, parsePublicRawRSAKeyHex: function (r) { var u = ASN1HEX; var t = u.getChildIdx; var s = u.getV; var p = {}; if (r.substr(0, 2) != \"30\") { throw \"malformed RSA key(code:001)\" } var q = t(r, 0); if (q.length != 2) { throw \"malformed RSA key(code:002)\" } if (r.substr(q[0], 2) != \"02\") { throw \"malformed RSA key(code:003)\" } p.n = s(r, q[0]); if (r.substr(q[1], 2) != \"02\") { throw \"malformed RSA key(code:004)\" } p.e = s(r, q[1]); return p }, parsePublicPKCS8Hex: function (t) { var v = ASN1HEX; var u = v.getChildIdx; var s = v.getV; var q = {}; q.algparam = null; var r = u(t, 0); if (r.length != 2) { throw \"outer DERSequence shall have 2 elements: \" + r.length } var w = r[0]; if (t.substr(w, 2) != \"30\") { throw \"malformed PKCS8 public key(code:001)\" } var p = u(t, w); if (p.length != 2) { throw \"malformed PKCS8 public key(code:002)\" } if (t.substr(p[0], 2) != \"06\") { throw \"malformed PKCS8 public key(code:003)\" } q.algoid = s(t, p[0]); if (t.substr(p[1], 2) == \"06\") { q.algparam = s(t, p[1]) } else { if (t.substr(p[1], 2) == \"30\") { q.algparam = {}; q.algparam.p = v.getVbyList(t, p[1], [0], \"02\"); q.algparam.q = v.getVbyList(t, p[1], [1], \"02\"); q.algparam.g = v.getVbyList(t, p[1], [2], \"02\") } } if (t.substr(r[1], 2) != \"03\") { throw \"malformed PKCS8 public key(code:004)\" } q.key = s(t, r[1]).substr(2); return q }, } }(); KEYUTIL.getKey = function (l, k, n) { var G = ASN1HEX, L = G.getChildIdx, v = G.getV, d = G.getVbyList, c = KJUR.crypto, i = c.ECDSA, C = c.DSA, w = RSAKey, M = pemtohex, F = KEYUTIL; if (typeof w != \"undefined\" && l instanceof w) { return l } if (typeof i != \"undefined\" && l instanceof i) { return l } if (typeof C != \"undefined\" && l instanceof C) { return l } if (l.curve !== undefined && l.xy !== undefined && l.d === undefined) { return new i({ pub: l.xy, curve: l.curve }) } if (l.curve !== undefined && l.d !== undefined) { return new i({ prv: l.d, curve: l.curve }) } if (l.kty === undefined && l.n !== undefined && l.e !== undefined && l.d === undefined) { var P = new w(); P.setPublic(l.n, l.e); return P } if (l.kty === undefined && l.n !== undefined && l.e !== undefined && l.d !== undefined && l.p !== undefined && l.q !== undefined && l.dp !== undefined && l.dq !== undefined && l.co !== undefined && l.qi === undefined) { var P = new w(); P.setPrivateEx(l.n, l.e, l.d, l.p, l.q, l.dp, l.dq, l.co); return P } if (l.kty === undefined && l.n !== undefined && l.e !== undefined && l.d !== undefined && l.p === undefined) { var P = new w(); P.setPrivate(l.n, l.e, l.d); return P } if (l.p !== undefined && l.q !== undefined && l.g !== undefined && l.y !== undefined && l.x === undefined) { var P = new C(); P.setPublic(l.p, l.q, l.g, l.y); return P } if (l.p !== undefined && l.q !== undefined && l.g !== undefined && l.y !== undefined && l.x !== undefined) { var P = new C(); P.setPrivate(l.p, l.q, l.g, l.y, l.x); return P } if (l.kty === \"RSA\" && l.n !== undefined && l.e !== undefined && l.d === undefined) { var P = new w(); P.setPublic(b64utohex(l.n), b64utohex(l.e)); return P } if (l.kty === \"RSA\" && l.n !== undefined && l.e !== undefined && l.d !== undefined && l.p !== undefined && l.q !== undefined && l.dp !== undefined && l.dq !== undefined && l.qi !== undefined) { var P = new w(); P.setPrivateEx(b64utohex(l.n), b64utohex(l.e), b64utohex(l.d), b64utohex(l.p), b64utohex(l.q), b64utohex(l.dp), b64utohex(l.dq), b64utohex(l.qi)); return P } if (l.kty === \"RSA\" && l.n !== undefined && l.e !== undefined && l.d !== undefined) { var P = new w(); P.setPrivate(b64utohex(l.n), b64utohex(l.e), b64utohex(l.d)); return P } if (l.kty === \"EC\" && l.crv !== undefined && l.x !== undefined && l.y !== undefined && l.d === undefined) { var j = new i({ curve: l.crv }); var t = j.ecparams.keylen / 4; var B = (\"0000000000\" + b64utohex(l.x)).slice(-t); var z = (\"0000000000\" + b64utohex(l.y)).slice(-t); var u = \"04\" + B + z; j.setPublicKeyHex(u); return j } if (l.kty === \"EC\" && l.crv !== undefined && l.x !== undefined && l.y !== undefined && l.d !== undefined) { var j = new i({ curve: l.crv }); var t = j.ecparams.keylen / 4; var B = (\"0000000000\" + b64utohex(l.x)).slice(-t); var z = (\"0000000000\" + b64utohex(l.y)).slice(-t); var u = \"04\" + B + z; var b = (\"0000000000\" + b64utohex(l.d)).slice(-t); j.setPublicKeyHex(u); j.setPrivateKeyHex(b); return j } if (n === \"pkcs5prv\") { var J = l, G = ASN1HEX, N, P; N = L(J, 0); if (N.length === 9) { P = new w(); P.readPKCS5PrvKeyHex(J) } else { if (N.length === 6) { P = new C(); P.readPKCS5PrvKeyHex(J) } else { if (N.length > 2 && J.substr(N[1], 2) === \"04\") { P = new i(); P.readPKCS5PrvKeyHex(J) } else { throw \"unsupported PKCS#1/5 hexadecimal key\" } } } return P } if (n === \"pkcs8prv\") { var P = F.getKeyFromPlainPrivatePKCS8Hex(l); return P } if (n === \"pkcs8pub\") { return F._getKeyFromPublicPKCS8Hex(l) } if (n === \"x509pub\") { return X509.getPublicKeyFromCertHex(l) } if (l.indexOf(\"-END CERTIFICATE-\", 0) != -1 || l.indexOf(\"-END X509 CERTIFICATE-\", 0) != -1 || l.indexOf(\"-END TRUSTED CERTIFICATE-\", 0) != -1) { return X509.getPublicKeyFromCertPEM(l) } if (l.indexOf(\"-END PUBLIC KEY-\") != -1) { var O = pemtohex(l, \"PUBLIC KEY\"); return F._getKeyFromPublicPKCS8Hex(O) } if (l.indexOf(\"-END RSA PRIVATE KEY-\") != -1 && l.indexOf(\"4,ENCRYPTED\") == -1) { var m = M(l, \"RSA PRIVATE KEY\"); return F.getKey(m, null, \"pkcs5prv\") } if (l.indexOf(\"-END DSA PRIVATE KEY-\") != -1 && l.indexOf(\"4,ENCRYPTED\") == -1) { var I = M(l, \"DSA PRIVATE KEY\"); var E = d(I, 0, [1], \"02\"); var D = d(I, 0, [2], \"02\"); var K = d(I, 0, [3], \"02\"); var r = d(I, 0, [4], \"02\"); var s = d(I, 0, [5], \"02\"); var P = new C(); P.setPrivate(new BigInteger(E, 16), new BigInteger(D, 16), new BigInteger(K, 16), new BigInteger(r, 16), new BigInteger(s, 16)); return P } if (l.indexOf(\"-END PRIVATE KEY-\") != -1) { return F.getKeyFromPlainPrivatePKCS8PEM(l) } if (l.indexOf(\"-END RSA PRIVATE KEY-\") != -1 && l.indexOf(\"4,ENCRYPTED\") != -1) { var o = F.getDecryptedKeyHex(l, k); var H = new RSAKey(); H.readPKCS5PrvKeyHex(o); return H } if (l.indexOf(\"-END EC PRIVATE KEY-\") != -1 && l.indexOf(\"4,ENCRYPTED\") != -1) { var I = F.getDecryptedKeyHex(l, k); var P = d(I, 0, [1], \"04\"); var f = d(I, 0, [2, 0], \"06\"); var A = d(I, 0, [3, 0], \"03\").substr(2); var e = \"\"; if (KJUR.crypto.OID.oidhex2name[f] !== undefined) { e = KJUR.crypto.OID.oidhex2name[f] } else { throw \"undefined OID(hex) in KJUR.crypto.OID: \" + f } var j = new i({ curve: e }); j.setPublicKeyHex(A); j.setPrivateKeyHex(P); j.isPublic = false; return j } if (l.indexOf(\"-END DSA PRIVATE KEY-\") != -1 && l.indexOf(\"4,ENCRYPTED\") != -1) { var I = F.getDecryptedKeyHex(l, k); var E = d(I, 0, [1], \"02\"); var D = d(I, 0, [2], \"02\"); var K = d(I, 0, [3], \"02\"); var r = d(I, 0, [4], \"02\"); var s = d(I, 0, [5], \"02\"); var P = new C(); P.setPrivate(new BigInteger(E, 16), new BigInteger(D, 16), new BigInteger(K, 16), new BigInteger(r, 16), new BigInteger(s, 16)); return P } if (l.indexOf(\"-END ENCRYPTED PRIVATE KEY-\") != -1) { return F.getKeyFromEncryptedPKCS8PEM(l, k) } throw \"not supported argument\" }; KEYUTIL.generateKeypair = function (a, c) { if (a == \"RSA\") { var b = c; var h = new RSAKey(); h.generate(b, \"10001\"); h.isPrivate = true; h.isPublic = true; var f = new RSAKey(); var e = h.n.toString(16); var i = h.e.toString(16); f.setPublic(e, i); f.isPrivate = false; f.isPublic = true; var k = {}; k.prvKeyObj = h; k.pubKeyObj = f; return k } else { if (a == \"EC\") { var d = c; var g = new KJUR.crypto.ECDSA({ curve: d }); var j = g.generateKeyPairHex(); var h = new KJUR.crypto.ECDSA({ curve: d }); h.setPublicKeyHex(j.ecpubhex); h.setPrivateKeyHex(j.ecprvhex); h.isPrivate = true; h.isPublic = false; var f = new KJUR.crypto.ECDSA({ curve: d }); f.setPublicKeyHex(j.ecpubhex); f.isPrivate = false; f.isPublic = true; var k = {}; k.prvKeyObj = h; k.pubKeyObj = f; return k } else { throw \"unknown algorithm: \" + a } } }; KEYUTIL.getPEM = function (b, D, y, m, q, j) { var F = KJUR, k = F.asn1, z = k.DERObjectIdentifier, f = k.DERInteger, l = k.ASN1Util.newObject, a = k.x509, C = a.SubjectPublicKeyInfo, e = F.crypto, u = e.DSA, r = e.ECDSA, n = RSAKey; function A(s) { var G = l({ seq: [{ \"int\": 0 }, { \"int\": { bigint: s.n } }, { \"int\": s.e }, { \"int\": { bigint: s.d } }, { \"int\": { bigint: s.p } }, { \"int\": { bigint: s.q } }, { \"int\": { bigint: s.dmp1 } }, { \"int\": { bigint: s.dmq1 } }, { \"int\": { bigint: s.coeff } }] }); return G } function B(G) { var s = l({ seq: [{ \"int\": 1 }, { octstr: { hex: G.prvKeyHex } }, { tag: [\"a0\", true, { oid: { name: G.curveName } }] }, { tag: [\"a1\", true, { bitstr: { hex: \"00\" + G.pubKeyHex } }] }] }); return s } function x(s) { var G = l({ seq: [{ \"int\": 0 }, { \"int\": { bigint: s.p } }, { \"int\": { bigint: s.q } }, { \"int\": { bigint: s.g } }, { \"int\": { bigint: s.y } }, { \"int\": { bigint: s.x } }] }); return G } if (((n !== undefined && b instanceof n) || (u !== undefined && b instanceof u) || (r !== undefined && b instanceof r)) && b.isPublic == true && (D === undefined || D == \"PKCS8PUB\")) { var E = new C(b); var w = E.getEncodedHex(); return hextopem(w, \"PUBLIC KEY\") } if (D == \"PKCS1PRV\" && n !== undefined && b instanceof n && (y === undefined || y == null) && b.isPrivate == true) { var E = A(b); var w = E.getEncodedHex(); return hextopem(w, \"RSA PRIVATE KEY\") } if (D == \"PKCS1PRV\" && r !== undefined && b instanceof r && (y === undefined || y == null) && b.isPrivate == true) { var i = new z({ name: b.curveName }); var v = i.getEncodedHex(); var h = B(b); var t = h.getEncodedHex(); var p = \"\"; p += hextopem(v, \"EC PARAMETERS\"); p += hextopem(t, \"EC PRIVATE KEY\"); return p } if (D == \"PKCS1PRV\" && u !== undefined && b instanceof u && (y === undefined || y == null) && b.isPrivate == true) { var E = x(b); var w = E.getEncodedHex(); return hextopem(w, \"DSA PRIVATE KEY\") } if (D == \"PKCS5PRV\" && n !== undefined && b instanceof n && (y !== undefined && y != null) && b.isPrivate == true) { var E = A(b); var w = E.getEncodedHex(); if (m === undefined) { m = \"DES-EDE3-CBC\" } return this.getEncryptedPKCS5PEMFromPrvKeyHex(\"RSA\", w, y, m, j) } if (D == \"PKCS5PRV\" && r !== undefined && b instanceof r && (y !== undefined && y != null) && b.isPrivate == true) { var E = B(b); var w = E.getEncodedHex(); if (m === undefined) { m = \"DES-EDE3-CBC\" } return this.getEncryptedPKCS5PEMFromPrvKeyHex(\"EC\", w, y, m, j) } if (D == \"PKCS5PRV\" && u !== undefined && b instanceof u && (y !== undefined && y != null) && b.isPrivate == true) { var E = x(b); var w = E.getEncodedHex(); if (m === undefined) { m = \"DES-EDE3-CBC\" } return this.getEncryptedPKCS5PEMFromPrvKeyHex(\"DSA\", w, y, m, j) } var o = function (G, s) { var I = c(G, s); var H = new l({ seq: [{ seq: [{ oid: { name: \"pkcs5PBES2\" } }, { seq: [{ seq: [{ oid: { name: \"pkcs5PBKDF2\" } }, { seq: [{ octstr: { hex: I.pbkdf2Salt } }, { \"int\": I.pbkdf2Iter }] }] }, { seq: [{ oid: { name: \"des-EDE3-CBC\" } }, { octstr: { hex: I.encryptionSchemeIV } }] }] }] }, { octstr: { hex: I.ciphertext } }] }); return H.getEncodedHex() }; var c = function (N, O) { var H = 100; var M = CryptoJS.lib.WordArray.random(8); var L = \"DES-EDE3-CBC\"; var s = CryptoJS.lib.WordArray.random(8); var I = CryptoJS.PBKDF2(O, M, { keySize: 192 / 32, iterations: H }); var J = CryptoJS.enc.Hex.parse(N); var K = CryptoJS.TripleDES.encrypt(J, I, { iv: s }) + \"\"; var G = {}; G.ciphertext = K; G.pbkdf2Salt = CryptoJS.enc.Hex.stringify(M); G.pbkdf2Iter = H; G.encryptionSchemeAlg = L; G.encryptionSchemeIV = CryptoJS.enc.Hex.stringify(s); return G }; if (D == \"PKCS8PRV\" && n != undefined && b instanceof n && b.isPrivate == true) { var g = A(b); var d = g.getEncodedHex(); var E = l({ seq: [{ \"int\": 0 }, { seq: [{ oid: { name: \"rsaEncryption\" } }, { \"null\": true }] }, { octstr: { hex: d } }] }); var w = E.getEncodedHex(); if (y === undefined || y == null) { return hextopem(w, \"PRIVATE KEY\") } else { var t = o(w, y); return hextopem(t, \"ENCRYPTED PRIVATE KEY\") } } if (D == \"PKCS8PRV\" && r !== undefined && b instanceof r && b.isPrivate == true) { var g = new l({ seq: [{ \"int\": 1 }, { octstr: { hex: b.prvKeyHex } }, { tag: [\"a1\", true, { bitstr: { hex: \"00\" + b.pubKeyHex } }] }] }); var d = g.getEncodedHex(); var E = l({ seq: [{ \"int\": 0 }, { seq: [{ oid: { name: \"ecPublicKey\" } }, { oid: { name: b.curveName } }] }, { octstr: { hex: d } }] }); var w = E.getEncodedHex(); if (y === undefined || y == null) { return hextopem(w, \"PRIVATE KEY\") } else { var t = o(w, y); return hextopem(t, \"ENCRYPTED PRIVATE KEY\") } } if (D == \"PKCS8PRV\" && u !== undefined && b instanceof u && b.isPrivate == true) { var g = new f({ bigint: b.x }); var d = g.getEncodedHex(); var E = l({ seq: [{ \"int\": 0 }, { seq: [{ oid: { name: \"dsa\" } }, { seq: [{ \"int\": { bigint: b.p } }, { \"int\": { bigint: b.q } }, { \"int\": { bigint: b.g } }] }] }, { octstr: { hex: d } }] }); var w = E.getEncodedHex(); if (y === undefined || y == null) { return hextopem(w, \"PRIVATE KEY\") } else { var t = o(w, y); return hextopem(t, \"ENCRYPTED PRIVATE KEY\") } } throw \"unsupported object nor format\" }; KEYUTIL.getKeyFromCSRPEM = function (b) { var a = pemtohex(b, \"CERTIFICATE REQUEST\"); var c = KEYUTIL.getKeyFromCSRHex(a); return c }; KEYUTIL.getKeyFromCSRHex = function (a) { var c = KEYUTIL.parseCSRHex(a); var b = KEYUTIL.getKey(c.p8pubkeyhex, null, \"pkcs8pub\"); return b }; KEYUTIL.parseCSRHex = function (d) { var i = ASN1HEX; var f = i.getChildIdx; var c = i.getTLV; var b = {}; var g = d; if (g.substr(0, 2) != \"30\") { throw \"malformed CSR(code:001)\" } var e = f(g, 0); if (e.length < 1) { throw \"malformed CSR(code:002)\" } if (g.substr(e[0], 2) != \"30\") { throw \"malformed CSR(code:003)\" } var a = f(g, e[0]); if (a.length < 3) { throw \"malformed CSR(code:004)\" } b.p8pubkeyhex = c(g, a[2]); return b }; KEYUTIL.getJWKFromKey = function (d) { var b = {}; if (d instanceof RSAKey && d.isPrivate) { b.kty = \"RSA\"; b.n = hextob64u(d.n.toString(16)); b.e = hextob64u(d.e.toString(16)); b.d = hextob64u(d.d.toString(16)); b.p = hextob64u(d.p.toString(16)); b.q = hextob64u(d.q.toString(16)); b.dp = hextob64u(d.dmp1.toString(16)); b.dq = hextob64u(d.dmq1.toString(16)); b.qi = hextob64u(d.coeff.toString(16)); return b } else { if (d instanceof RSAKey && d.isPublic) { b.kty = \"RSA\"; b.n = hextob64u(d.n.toString(16)); b.e = hextob64u(d.e.toString(16)); return b } else { if (d instanceof KJUR.crypto.ECDSA && d.isPrivate) { var a = d.getShortNISTPCurveName(); if (a !== \"P-256\" && a !== \"P-384\") { throw \"unsupported curve name for JWT: \" + a } var c = d.getPublicKeyXYHex(); b.kty = \"EC\"; b.crv = a; b.x = hextob64u(c.x); b.y = hextob64u(c.y); b.d = hextob64u(d.prvKeyHex); return b } else { if (d instanceof KJUR.crypto.ECDSA && d.isPublic) { var a = d.getShortNISTPCurveName(); if (a !== \"P-256\" && a !== \"P-384\") { throw \"unsupported curve name for JWT: \" + a } var c = d.getPublicKeyXYHex(); b.kty = \"EC\"; b.crv = a; b.x = hextob64u(c.x); b.y = hextob64u(c.y); return b } } } } throw \"not supported key object\" };\nRSAKey.getPosArrayOfChildrenFromHex = function (a) { return ASN1HEX.getChildIdx(a, 0) }; RSAKey.getHexValueArrayOfChildrenFromHex = function (f) { var n = ASN1HEX; var i = n.getV; var k = RSAKey.getPosArrayOfChildrenFromHex(f); var e = i(f, k[0]); var j = i(f, k[1]); var b = i(f, k[2]); var c = i(f, k[3]); var h = i(f, k[4]); var g = i(f, k[5]); var m = i(f, k[6]); var l = i(f, k[7]); var d = i(f, k[8]); var k = new Array(); k.push(e, j, b, c, h, g, m, l, d); return k }; RSAKey.prototype.readPrivateKeyFromPEMString = function (d) { var c = pemtohex(d); var b = RSAKey.getHexValueArrayOfChildrenFromHex(c); this.setPrivateEx(b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]) }; RSAKey.prototype.readPKCS5PrvKeyHex = function (c) { var b = RSAKey.getHexValueArrayOfChildrenFromHex(c); this.setPrivateEx(b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]) }; RSAKey.prototype.readPKCS8PrvKeyHex = function (e) { var c, j, l, b, a, f, d, k; var m = ASN1HEX; var g = m.getVbyList; if (m.isASN1HEX(e) === false) { throw \"not ASN.1 hex string\" } try { c = g(e, 0, [2, 0, 1], \"02\"); j = g(e, 0, [2, 0, 2], \"02\"); l = g(e, 0, [2, 0, 3], \"02\"); b = g(e, 0, [2, 0, 4], \"02\"); a = g(e, 0, [2, 0, 5], \"02\"); f = g(e, 0, [2, 0, 6], \"02\"); d = g(e, 0, [2, 0, 7], \"02\"); k = g(e, 0, [2, 0, 8], \"02\") } catch (i) { throw \"malformed PKCS#8 plain RSA private key\" } this.setPrivateEx(c, j, l, b, a, f, d, k) }; RSAKey.prototype.readPKCS5PubKeyHex = function (c) { var e = ASN1HEX; var b = e.getV; if (e.isASN1HEX(c) === false) { throw \"keyHex is not ASN.1 hex string\" } var a = e.getChildIdx(c, 0); if (a.length !== 2 || c.substr(a[0], 2) !== \"02\" || c.substr(a[1], 2) !== \"02\") { throw \"wrong hex for PKCS#5 public key\" } var f = b(c, a[0]); var d = b(c, a[1]); this.setPublic(f, d) }; RSAKey.prototype.readPKCS8PubKeyHex = function (b) { var c = ASN1HEX; if (c.isASN1HEX(b) === false) { throw \"not ASN.1 hex string\" } if (c.getTLVbyList(b, 0, [0, 0]) !== \"06092a864886f70d010101\") { throw \"not PKCS8 RSA public key\" } var a = c.getTLVbyList(b, 0, [1, 0]); this.readPKCS5PubKeyHex(a) }; RSAKey.prototype.readCertPubKeyHex = function (b, d) { var a, c; a = new X509(); a.readCertHex(b); c = a.getPublicKeyHex(); this.readPKCS8PubKeyHex(c) };\nvar _RE_HEXDECONLY = new RegExp(\"\"); _RE_HEXDECONLY.compile(\"[^0-9a-f]\", \"gi\"); function _rsasign_getHexPaddedDigestInfoForString(d, e, a) { var b = function (f) { return KJUR.crypto.Util.hashString(f, a) }; var c = b(d); return KJUR.crypto.Util.getPaddedDigestInfoHex(c, a, e) } function _zeroPaddingOfSignature(e, d) { var c = \"\"; var a = d / 4 - e.length; for (var b = 0; b < a; b++) { c = c + \"0\" } return c + e } RSAKey.prototype.sign = function (d, a) { var b = function (e) { return KJUR.crypto.Util.hashString(e, a) }; var c = b(d); return this.signWithMessageHash(c, a) }; RSAKey.prototype.signWithMessageHash = function (e, c) { var f = KJUR.crypto.Util.getPaddedDigestInfoHex(e, c, this.n.bitLength()); var b = parseBigInt(f, 16); var d = this.doPrivate(b); var a = d.toString(16); return _zeroPaddingOfSignature(a, this.n.bitLength()) }; function pss_mgf1_str(c, a, e) { var b = \"\", d = 0; while (b.length < a) { b += hextorstr(e(rstrtohex(c + String.fromCharCode.apply(String, [(d & 4278190080) >> 24, (d & 16711680) >> 16, (d & 65280) >> 8, d & 255])))); d += 1 } return b } RSAKey.prototype.signPSS = function (e, a, d) { var c = function (f) { return KJUR.crypto.Util.hashHex(f, a) }; var b = c(rstrtohex(e)); if (d === undefined) { d = -1 } return this.signWithMessageHashPSS(b, a, d) }; RSAKey.prototype.signWithMessageHashPSS = function (l, a, k) { var b = hextorstr(l); var g = b.length; var m = this.n.bitLength() - 1; var c = Math.ceil(m / 8); var d; var o = function (i) { return KJUR.crypto.Util.hashHex(i, a) }; if (k === -1 || k === undefined) { k = g } else { if (k === -2) { k = c - g - 2 } else { if (k < -2) { throw \"invalid salt length\" } } } if (c < (g + k + 2)) { throw \"data too long\" } var f = \"\"; if (k > 0) { f = new Array(k); new SecureRandom().nextBytes(f); f = String.fromCharCode.apply(String, f) } var n = hextorstr(o(rstrtohex(\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\" + b + f))); var j = []; for (d = 0; d < c - k - g - 2; d += 1) { j[d] = 0 } var e = String.fromCharCode.apply(String, j) + \"\\x01\" + f; var h = pss_mgf1_str(n, e.length, o); var q = []; for (d = 0; d < e.length; d += 1) { q[d] = e.charCodeAt(d) ^ h.charCodeAt(d) } var p = (65280 >> (8 * c - m)) & 255; q[0] &= ~p; for (d = 0; d < g; d++) { q.push(n.charCodeAt(d)) } q.push(188); return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(q)).toString(16), this.n.bitLength()) }; function _rsasign_getDecryptSignatureBI(a, d, c) { var b = new RSAKey(); b.setPublic(d, c); var e = b.doPublic(a); return e } function _rsasign_getHexDigestInfoFromSig(a, c, b) { var e = _rsasign_getDecryptSignatureBI(a, c, b); var d = e.toString(16).replace(/^1f+00/, \"\"); return d } function _rsasign_getAlgNameAndHashFromHexDisgestInfo(f) { for (var e in KJUR.crypto.Util.DIGESTINFOHEAD) { var d = KJUR.crypto.Util.DIGESTINFOHEAD[e]; var b = d.length; if (f.substring(0, b) == d) { var c = [e, f.substring(b)]; return c } } return [] } RSAKey.prototype.verify = function (f, j) { j = j.replace(_RE_HEXDECONLY, \"\"); j = j.replace(/[ \\n]+/g, \"\"); var b = parseBigInt(j, 16); if (b.bitLength() > this.n.bitLength()) { return 0 } var i = this.doPublic(b); var e = i.toString(16).replace(/^1f+00/, \"\"); var g = _rsasign_getAlgNameAndHashFromHexDisgestInfo(e); if (g.length == 0) { return false } var d = g[0]; var h = g[1]; var a = function (k) { return KJUR.crypto.Util.hashString(k, d) }; var c = a(f); return (h == c) }; RSAKey.prototype.verifyWithMessageHash = function (e, a) { a = a.replace(_RE_HEXDECONLY, \"\"); a = a.replace(/[ \\n]+/g, \"\"); var b = parseBigInt(a, 16); if (b.bitLength() > this.n.bitLength()) { return 0 } var h = this.doPublic(b); var g = h.toString(16).replace(/^1f+00/, \"\"); var c = _rsasign_getAlgNameAndHashFromHexDisgestInfo(g); if (c.length == 0) { return false } var d = c[0]; var f = c[1]; return (f == e) }; RSAKey.prototype.verifyPSS = function (c, b, a, f) { var e = function (g) { return KJUR.crypto.Util.hashHex(g, a) }; var d = e(rstrtohex(c)); if (f === undefined) { f = -1 } return this.verifyWithMessageHashPSS(d, b, a, f) }; RSAKey.prototype.verifyWithMessageHashPSS = function (f, s, l, c) { var k = new BigInteger(s, 16); if (k.bitLength() > this.n.bitLength()) { return false } var r = function (i) { return KJUR.crypto.Util.hashHex(i, l) }; var j = hextorstr(f); var h = j.length; var g = this.n.bitLength() - 1; var m = Math.ceil(g / 8); var q; if (c === -1 || c === undefined) { c = h } else { if (c === -2) { c = m - h - 2 } else { if (c < -2) { throw \"invalid salt length\" } } } if (m < (h + c + 2)) { throw \"data too long\" } var a = this.doPublic(k).toByteArray(); for (q = 0; q < a.length; q += 1) { a[q] &= 255 } while (a.length < m) { a.unshift(0) } if (a[m - 1] !== 188) { throw \"encoded message does not end in 0xbc\" } a = String.fromCharCode.apply(String, a); var d = a.substr(0, m - h - 1); var e = a.substr(d.length, h); var p = (65280 >> (8 * m - g)) & 255; if ((d.charCodeAt(0) & p) !== 0) { throw \"bits beyond keysize not zero\" } var n = pss_mgf1_str(e, d.length, r); var o = []; for (q = 0; q < d.length; q += 1) { o[q] = d.charCodeAt(q) ^ n.charCodeAt(q) } o[0] &= ~p; var b = m - h - c - 2; for (q = 0; q < b; q += 1) { if (o[q] !== 0) { throw \"leftmost octets not zero\" } } if (o[b] !== 1) { throw \"0x01 marker not found\" } return e === hextorstr(r(rstrtohex(\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\" + j + String.fromCharCode.apply(String, o.slice(-c))))) }; RSAKey.SALT_LEN_HLEN = -1; RSAKey.SALT_LEN_MAX = -2; RSAKey.SALT_LEN_RECOVER = -2;\nfunction X509() { var k = ASN1HEX, j = k.getChildIdx, h = k.getV, b = k.getTLV, f = k.getVbyList, c = k.getTLVbyList, g = k.getIdxbyList, d = k.getVidx, i = k.oidname, a = X509, e = pemtohex; this.hex = null; this.version = 0; this.foffset = 0; this.aExtInfo = null; this.getVersion = function () { if (this.hex === null || this.version !== 0) { return this.version } if (c(this.hex, 0, [0, 0]) !== \"a003020102\") { this.version = 1; this.foffset = -1; return 1 } this.version = 3; return 3 }; this.getSerialNumberHex = function () { return f(this.hex, 0, [0, 1 + this.foffset], \"02\") }; this.getSignatureAlgorithmField = function () { return i(f(this.hex, 0, [0, 2 + this.foffset, 0], \"06\")) }; this.getIssuerHex = function () { return c(this.hex, 0, [0, 3 + this.foffset], \"30\") }; this.getIssuerString = function () { return a.hex2dn(this.getIssuerHex()) }; this.getSubjectHex = function () { return c(this.hex, 0, [0, 5 + this.foffset], \"30\") }; this.getSubjectString = function () { return a.hex2dn(this.getSubjectHex()) }; this.getNotBefore = function () { var l = f(this.hex, 0, [0, 4 + this.foffset, 0]); l = l.replace(/(..)/g, \"%$1\"); l = decodeURIComponent(l); return l }; this.getNotAfter = function () { var l = f(this.hex, 0, [0, 4 + this.foffset, 1]); l = l.replace(/(..)/g, \"%$1\"); l = decodeURIComponent(l); return l }; this.getPublicKeyHex = function () { return k.getTLVbyList(this.hex, 0, [0, 6 + this.foffset], \"30\") }; this.getPublicKeyIdx = function () { return g(this.hex, 0, [0, 6 + this.foffset], \"30\") }; this.getPublicKeyContentIdx = function () { var l = this.getPublicKeyIdx(); return g(this.hex, l, [1, 0], \"30\") }; this.getPublicKey = function () { return KEYUTIL.getKey(this.getPublicKeyHex(), null, \"pkcs8pub\") }; this.getSignatureAlgorithmName = function () { return i(f(this.hex, 0, [1, 0], \"06\")) }; this.getSignatureValueHex = function () { return f(this.hex, 0, [2], \"03\", true) }; this.verifySignature = function (n) { var o = this.getSignatureAlgorithmName(); var l = this.getSignatureValueHex(); var m = c(this.hex, 0, [0], \"30\"); var p = new KJUR.crypto.Signature({ alg: o }); p.init(n); p.updateHex(m); return p.verify(l) }; this.parseExt = function () { if (this.version !== 3) { return -1 } var p = g(this.hex, 0, [0, 7, 0], \"30\"); var m = j(this.hex, p); this.aExtInfo = new Array(); for (var n = 0; n < m.length; n++) { var q = {}; q.critical = false; var l = j(this.hex, m[n]); var r = 0; if (l.length === 3) { q.critical = true; r = 1 } q.oid = k.hextooidstr(f(this.hex, m[n], [0], \"06\")); var o = g(this.hex, m[n], [1 + r]); q.vidx = d(this.hex, o); this.aExtInfo.push(q) } }; this.getExtInfo = function (n) { var l = this.aExtInfo; var o = n; if (!n.match(/^[0-9.]+$/)) { o = KJUR.asn1.x509.OID.name2oid(n) } if (o === \"\") { return undefined } for (var m = 0; m < l.length; m++) { if (l[m].oid === o) { return l[m] } } return undefined }; this.getExtBasicConstraints = function () { var n = this.getExtInfo(\"basicConstraints\"); if (n === undefined) { return n } var l = h(this.hex, n.vidx); if (l === \"\") { return {} } if (l === \"0101ff\") { return { cA: true } } if (l.substr(0, 8) === \"0101ff02\") { var o = h(l, 6); var m = parseInt(o, 16); return { cA: true, pathLen: m } } throw \"basicConstraints parse error\" }; this.getExtKeyUsageBin = function () { var o = this.getExtInfo(\"keyUsage\"); if (o === undefined) { return \"\" } var m = h(this.hex, o.vidx); if (m.length % 2 != 0 || m.length <= 2) { throw \"malformed key usage value\" } var l = parseInt(m.substr(0, 2)); var n = parseInt(m.substr(2), 16).toString(2); return n.substr(0, n.length - l) }; this.getExtKeyUsageString = function () { var n = this.getExtKeyUsageBin(); var l = new Array(); for (var m = 0; m < n.length; m++) { if (n.substr(m, 1) == \"1\") { l.push(X509.KEYUSAGE_NAME[m]) } } return l.join(\",\") }; this.getExtSubjectKeyIdentifier = function () { var l = this.getExtInfo(\"subjectKeyIdentifier\"); if (l === undefined) { return l } return h(this.hex, l.vidx) }; this.getExtAuthorityKeyIdentifier = function () { var p = this.getExtInfo(\"authorityKeyIdentifier\"); if (p === undefined) { return p } var l = {}; var o = b(this.hex, p.vidx); var m = j(o, 0); for (var n = 0; n < m.length; n++) { if (o.substr(m[n], 2) === \"80\") { l.kid = h(o, m[n]) } } return l }; this.getExtExtKeyUsageName = function () { var p = this.getExtInfo(\"extKeyUsage\"); if (p === undefined) { return p } var l = new Array(); var o = b(this.hex, p.vidx); if (o === \"\") { return l } var m = j(o, 0); for (var n = 0; n < m.length; n++) { l.push(i(h(o, m[n]))) } return l }; this.getExtSubjectAltName = function () { var m = this.getExtSubjectAltName2(); var l = new Array(); for (var n = 0; n < m.length; n++) { if (m[n][0] === \"DNS\") { l.push(m[n][1]) } } return l }; this.getExtSubjectAltName2 = function () { var l, p, n; var m = this.getExtInfo(\"subjectAltName\"); if (m === undefined) { return m } var t = new Array(); var q = b(this.hex, m.vidx); var s = j(q, 0); for (var o = 0; o < s.length; o++) { n = q.substr(s[o], 2); l = h(q, s[o]); if (n === \"81\") { p = hextoutf8(l); t.push([\"MAIL\", p]) } if (n === \"82\") { p = hextoutf8(l); t.push([\"DNS\", p]) } if (n === \"84\") { p = X509.hex2dn(l, 0); t.push([\"DN\", p]) } if (n === \"86\") { p = hextoutf8(l); t.push([\"URI\", p]) } if (n === \"87\") { try { p = parseInt(p.substr(0, 2), 16) + \".\" + parseInt(p.substr(2, 2), 16) + \".\" + parseInt(p.substr(4, 2), 16) + \".\" + parseInt(p.substr(6, 2), 16); t.push([\"IP\", p]) } catch (r) { } } } return t }; this.getExtCRLDistributionPointsURI = function () { var q = this.getExtInfo(\"cRLDistributionPoints\"); if (q === undefined) { return q } var l = new Array(); var m = j(this.hex, q.vidx); for (var o = 0; o < m.length; o++) { try { var r = f(this.hex, m[o], [0, 0, 0], \"86\"); var p = hextoutf8(r); l.push(p) } catch (n) { } } return l }; this.getExtAIAInfo = function () { var p = this.getExtInfo(\"authorityInfoAccess\"); if (p === undefined) { return p } var l = { ocsp: [], caissuer: [] }; var m = j(this.hex, p.vidx); for (var n = 0; n < m.length; n++) { var q = f(this.hex, m[n], [0], \"06\"); var o = f(this.hex, m[n], [1], \"86\"); if (q === \"2b06010505073001\") { l.ocsp.push(hextoutf8(o)) } if (q === \"2b06010505073002\") { l.caissuer.push(hextoutf8(o)) } } return l }; this.getExtCertificatePolicies = function () { var o = this.getExtInfo(\"certificatePolicies\"); if (o === undefined) { return o } var l = b(this.hex, o.vidx); var u = []; var s = j(l, 0); for (var r = 0; r < s.length; r++) { var t = {}; var n = j(l, s[r]); t.id = i(h(l, n[0])); if (n.length === 2) { var m = j(l, n[1]); for (var q = 0; q < m.length; q++) { var p = f(l, m[q], [0], \"06\"); if (p === \"2b06010505070201\") { t.cps = hextoutf8(f(l, m[q], [1])) } else { if (p === \"2b06010505070202\") { t.unotice = hextoutf8(f(l, m[q], [1, 0])) } } } } u.push(t) } return u }; this.readCertPEM = function (l) { this.readCertHex(e(l)) }; this.readCertHex = function (l) { this.hex = l; this.getVersion(); try { g(this.hex, 0, [0, 7], \"a3\"); this.parseExt() } catch (m) { } }; this.getInfo = function () { var m = X509; var B, u, z; B = \"Basic Fields\\n\"; B += \"  serial number: \" + this.getSerialNumberHex() + \"\\n\"; B += \"  signature algorithm: \" + this.getSignatureAlgorithmField() + \"\\n\"; B += \"  issuer: \" + this.getIssuerString() + \"\\n\"; B += \"  notBefore: \" + this.getNotBefore() + \"\\n\"; B += \"  notAfter: \" + this.getNotAfter() + \"\\n\"; B += \"  subject: \" + this.getSubjectString() + \"\\n\"; B += \"  subject public key info: \\n\"; u = this.getPublicKey(); B += \"    key algorithm: \" + u.type + \"\\n\"; if (u.type === \"RSA\") { B += \"    n=\" + hextoposhex(u.n.toString(16)).substr(0, 16) + \"...\\n\"; B += \"    e=\" + hextoposhex(u.e.toString(16)) + \"\\n\" } z = this.aExtInfo; if (z !== undefined && z !== null) { B += \"X509v3 Extensions:\\n\"; for (var r = 0; r < z.length; r++) { var n = z[r]; var A = KJUR.asn1.x509.OID.oid2name(n.oid); if (A === \"\") { A = n.oid } var x = \"\"; if (n.critical === true) { x = \"CRITICAL\" } B += \"  \" + A + \" \" + x + \":\\n\"; if (A === \"basicConstraints\") { var v = this.getExtBasicConstraints(); if (v.cA === undefined) { B += \"    {}\\n\" } else { B += \"    cA=true\"; if (v.pathLen !== undefined) { B += \", pathLen=\" + v.pathLen } B += \"\\n\" } } else { if (A === \"keyUsage\") { B += \"    \" + this.getExtKeyUsageString() + \"\\n\" } else { if (A === \"subjectKeyIdentifier\") { B += \"    \" + this.getExtSubjectKeyIdentifier() + \"\\n\" } else { if (A === \"authorityKeyIdentifier\") { var l = this.getExtAuthorityKeyIdentifier(); if (l.kid !== undefined) { B += \"    kid=\" + l.kid + \"\\n\" } } else { if (A === \"extKeyUsage\") { var w = this.getExtExtKeyUsageName(); B += \"    \" + w.join(\", \") + \"\\n\" } else { if (A === \"subjectAltName\") { var t = this.getExtSubjectAltName2(); B += \"    \" + t + \"\\n\" } else { if (A === \"cRLDistributionPoints\") { var y = this.getExtCRLDistributionPointsURI(); B += \"    \" + y + \"\\n\" } else { if (A === \"authorityInfoAccess\") { var p = this.getExtAIAInfo(); if (p.ocsp !== undefined) { B += \"    ocsp: \" + p.ocsp.join(\",\") + \"\\n\" } if (p.caissuer !== undefined) { B += \"    caissuer: \" + p.caissuer.join(\",\") + \"\\n\" } } else { if (A === \"certificatePolicies\") { var o = this.getExtCertificatePolicies(); for (var q = 0; q < o.length; q++) { if (o[q].id !== undefined) { B += \"    policy oid: \" + o[q].id + \"\\n\" } if (o[q].cps !== undefined) { B += \"    cps: \" + o[q].cps + \"\\n\" } } } } } } } } } } } } } B += \"signature algorithm: \" + this.getSignatureAlgorithmName() + \"\\n\"; B += \"signature: \" + this.getSignatureValueHex().substr(0, 16) + \"...\\n\"; return B } } X509.hex2dn = function (f, b) { if (b === undefined) { b = 0 } if (f.substr(b, 2) !== \"30\") { throw \"malformed DN\" } var c = new Array(); var d = ASN1HEX.getChildIdx(f, b); for (var e = 0; e < d.length; e++) { c.push(X509.hex2rdn(f, d[e])) } c = c.map(function (a) { return a.replace(\"/\", \"\\\\/\") }); return \"/\" + c.join(\"/\") }; X509.hex2rdn = function (f, b) { if (b === undefined) { b = 0 } if (f.substr(b, 2) !== \"31\") { throw \"malformed RDN\" } var c = new Array(); var d = ASN1HEX.getChildIdx(f, b); for (var e = 0; e < d.length; e++) { c.push(X509.hex2attrTypeValue(f, d[e])) } c = c.map(function (a) { return a.replace(\"+\", \"\\\\+\") }); return c.join(\"+\") }; X509.hex2attrTypeValue = function (d, i) { var j = ASN1HEX; var h = j.getV; if (i === undefined) { i = 0 } if (d.substr(i, 2) !== \"30\") { throw \"malformed attribute type and value\" } var g = j.getChildIdx(d, i); if (g.length !== 2 || d.substr(g[0], 2) !== \"06\") { \"malformed attribute type and value\" } var b = h(d, g[0]); var f = KJUR.asn1.ASN1Util.oidHexToInt(b); var e = KJUR.asn1.x509.OID.oid2atype(f); var a = h(d, g[1]); var c = hextorstr(a); return e + \"=\" + c }; X509.getPublicKeyFromCertHex = function (b) { var a = new X509(); a.readCertHex(b); return a.getPublicKey() }; X509.getPublicKeyFromCertPEM = function (b) { var a = new X509(); a.readCertPEM(b); return a.getPublicKey() }; X509.getPublicKeyInfoPropOfCertPEM = function (c) { var e = ASN1HEX; var g = e.getVbyList; var b = {}; var a, f, d; b.algparam = null; a = new X509(); a.readCertPEM(c); f = a.getPublicKeyHex(); b.keyhex = g(f, 0, [1], \"03\").substr(2); b.algoid = g(f, 0, [0, 0], \"06\"); if (b.algoid === \"2a8648ce3d0201\") { b.algparam = g(f, 0, [0, 1], \"06\") } return b }; X509.KEYUSAGE_NAME = [\"digitalSignature\", \"nonRepudiation\", \"keyEncipherment\", \"dataEncipherment\", \"keyAgreement\", \"keyCertSign\", \"cRLSign\", \"encipherOnly\", \"decipherOnly\"];\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.jws == \"undefined\" || !KJUR.jws) { KJUR.jws = {} } KJUR.jws.JWS = function () { var b = KJUR, a = b.jws.JWS, c = a.isSafeJSONString; this.parseJWS = function (g, j) { if ((this.parsedJWS !== undefined) && (j || (this.parsedJWS.sigvalH !== undefined))) { return } var i = g.match(/^([^.]+)\\.([^.]+)\\.([^.]+)$/); if (i == null) { throw \"JWS signature is not a form of 'Head.Payload.SigValue'.\" } var k = i[1]; var e = i[2]; var l = i[3]; var n = k + \".\" + e; this.parsedJWS = {}; this.parsedJWS.headB64U = k; this.parsedJWS.payloadB64U = e; this.parsedJWS.sigvalB64U = l; this.parsedJWS.si = n; if (!j) { var h = b64utohex(l); var f = parseBigInt(h, 16); this.parsedJWS.sigvalH = h; this.parsedJWS.sigvalBI = f } var d = b64utoutf8(k); var m = b64utoutf8(e); this.parsedJWS.headS = d; this.parsedJWS.payloadS = m; if (!c(d, this.parsedJWS, \"headP\")) { throw \"malformed JSON string for JWS Head: \" + d } } }; KJUR.jws.JWS.sign = function (i, v, y, z, a) { var w = KJUR, m = w.jws, q = m.JWS, g = q.readSafeJSONString, p = q.isSafeJSONString, d = w.crypto, k = d.ECDSA, o = d.Mac, c = d.Signature, t = JSON; var s, j, n; if (typeof v != \"string\" && typeof v != \"object\") { throw \"spHeader must be JSON string or object: \" + v } if (typeof v == \"object\") { j = v; s = t.stringify(j) } if (typeof v == \"string\") { s = v; if (!p(s)) { throw \"JWS Head is not safe JSON string: \" + s } j = g(s) } n = y; if (typeof y == \"object\") { n = t.stringify(y) } if ((i == \"\" || i == null) && j.alg !== undefined) { i = j.alg } if ((i != \"\" && i != null) && j.alg === undefined) { j.alg = i; s = t.stringify(j) } if (i !== j.alg) { throw \"alg and sHeader.alg doesn't match: \" + i + \"!=\" + j.alg } var r = null; if (q.jwsalg2sigalg[i] === undefined) { throw \"unsupported alg name: \" + i } else { r = q.jwsalg2sigalg[i] } var e = utf8tob64u(s); var l = utf8tob64u(n); var b = e + \".\" + l; var x = \"\"; if (r.substr(0, 4) == \"Hmac\") { if (z === undefined) { throw \"mac key shall be specified for HS* alg\" } var h = new o({ alg: r, prov: \"cryptojs\", pass: z }); h.updateString(b); x = h.doFinal() } else { if (r.indexOf(\"withECDSA\") != -1) { var f = new c({ alg: r }); f.init(z, a); f.updateString(b); hASN1Sig = f.sign(); x = KJUR.crypto.ECDSA.asn1SigToConcatSig(hASN1Sig) } else { if (r != \"none\") { var f = new c({ alg: r }); f.init(z, a); f.updateString(b); x = f.sign() } } } var u = hextob64u(x); return b + \".\" + u }; KJUR.jws.JWS.verify = function (w, B, n) { var x = KJUR, q = x.jws, t = q.JWS, i = t.readSafeJSONString, e = x.crypto, p = e.ECDSA, s = e.Mac, d = e.Signature, m; if (typeof RSAKey !== undefined) { m = RSAKey } var y = w.split(\".\"); var f = y[0]; var r = y[1]; var c = f + \".\" + r; var A = b64utohex(y[2]); var l = i(b64utoutf8(y[0])); var k = null; var z = null; if (l.alg === undefined) { throw \"algorithm not specified in header\" } else { k = l.alg; z = k.substr(0, 2) } if (n != null && Object.prototype.toString.call(n) === \"[object Array]\" && n.length > 0) { var b = \":\" + n.join(\":\") + \":\"; if (b.indexOf(\":\" + k + \":\") == -1) { throw \"algorithm '\" + k + \"' not accepted in the list\" } } if (k != \"none\" && B === null) { throw \"key shall be specified to verify.\" } if (typeof B == \"string\" && B.indexOf(\"-----BEGIN \") != -1) { B = KEYUTIL.getKey(B) } if (z == \"RS\" || z == \"PS\") { if (!(B instanceof m)) { throw \"key shall be a RSAKey obj for RS* and PS* algs\" } } if (z == \"ES\") { if (!(B instanceof p)) { throw \"key shall be a ECDSA obj for ES* algs\" } } if (k == \"none\") { } var u = null; if (t.jwsalg2sigalg[l.alg] === undefined) { throw \"unsupported alg name: \" + k } else { u = t.jwsalg2sigalg[k] } if (u == \"none\") { throw \"not supported\" } else { if (u.substr(0, 4) == \"Hmac\") { var o = null; if (B === undefined) { throw \"hexadecimal key shall be specified for HMAC\" } var j = new s({ alg: u, pass: B }); j.updateString(c); o = j.doFinal(); return A == o } else { if (u.indexOf(\"withECDSA\") != -1) { var h = null; try { h = p.concatSigToASN1Sig(A) } catch (v) { return false } var g = new d({ alg: u }); g.init(B); g.updateString(c); return g.verify(h) } else { var g = new d({ alg: u }); g.init(B); g.updateString(c); return g.verify(A) } } } }; KJUR.jws.JWS.parse = function (g) { var c = g.split(\".\"); var b = {}; var f, e, d; if (c.length != 2 && c.length != 3) { throw \"malformed sJWS: wrong number of '.' splitted elements\" } f = c[0]; e = c[1]; if (c.length == 3) { d = c[2] } b.headerObj = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(f)); b.payloadObj = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(e)); b.headerPP = JSON.stringify(b.headerObj, null, \"  \"); if (b.payloadObj == null) { b.payloadPP = b64utoutf8(e) } else { b.payloadPP = JSON.stringify(b.payloadObj, null, \"  \") } if (d !== undefined) { b.sigHex = b64utohex(d) } return b }; KJUR.jws.JWS.verifyJWT = function (e, l, r) { var d = KJUR, j = d.jws, o = j.JWS, n = o.readSafeJSONString, p = o.inArray, f = o.includedArray; var k = e.split(\".\"); var c = k[0]; var i = k[1]; var q = c + \".\" + i; var m = b64utohex(k[2]); var h = n(b64utoutf8(c)); var g = n(b64utoutf8(i)); if (h.alg === undefined) { return false } if (r.alg === undefined) { throw \"acceptField.alg shall be specified\" } if (!p(h.alg, r.alg)) { return false } if (g.iss !== undefined && typeof r.iss === \"object\") { if (!p(g.iss, r.iss)) { return false } } if (g.sub !== undefined && typeof r.sub === \"object\") { if (!p(g.sub, r.sub)) { return false } } if (g.aud !== undefined && typeof r.aud === \"object\") { if (typeof g.aud == \"string\") { if (!p(g.aud, r.aud)) { return false } } else { if (typeof g.aud == \"object\") { if (!f(g.aud, r.aud)) { return false } } } } var b = j.IntDate.getNow(); if (r.verifyAt !== undefined && typeof r.verifyAt === \"number\") { b = r.verifyAt } if (r.gracePeriod === undefined || typeof r.gracePeriod !== \"number\") { r.gracePeriod = 0 } if (g.exp !== undefined && typeof g.exp == \"number\") { if (g.exp + r.gracePeriod < b) { return false } } if (g.nbf !== undefined && typeof g.nbf == \"number\") { if (b < g.nbf - r.gracePeriod) { return false } } if (g.iat !== undefined && typeof g.iat == \"number\") { if (b < g.iat - r.gracePeriod) { return false } } if (g.jti !== undefined && r.jti !== undefined) { if (g.jti !== r.jti) { return false } } if (!o.verify(e, l, r.alg)) { return false } return true }; KJUR.jws.JWS.includedArray = function (b, a) { var c = KJUR.jws.JWS.inArray; if (b === null) { return false } if (typeof b !== \"object\") { return false } if (typeof b.length !== \"number\") { return false } for (var d = 0; d < b.length; d++) { if (!c(b[d], a)) { return false } } return true }; KJUR.jws.JWS.inArray = function (d, b) { if (b === null) { return false } if (typeof b !== \"object\") { return false } if (typeof b.length !== \"number\") { return false } for (var c = 0; c < b.length; c++) { if (b[c] == d) { return true } } return false }; KJUR.jws.JWS.jwsalg2sigalg = { HS256: \"HmacSHA256\", HS384: \"HmacSHA384\", HS512: \"HmacSHA512\", RS256: \"SHA256withRSA\", RS384: \"SHA384withRSA\", RS512: \"SHA512withRSA\", ES256: \"SHA256withECDSA\", ES384: \"SHA384withECDSA\", PS256: \"SHA256withRSAandMGF1\", PS384: \"SHA384withRSAandMGF1\", PS512: \"SHA512withRSAandMGF1\", none: \"none\", }; KJUR.jws.JWS.isSafeJSONString = function (c, b, d) { var e = null; try { e = jsonParse(c); if (typeof e != \"object\") { return 0 } if (e.constructor === Array) { return 0 } if (b) { b[d] = e } return 1 } catch (a) { return 0 } }; KJUR.jws.JWS.readSafeJSONString = function (b) { var c = null; try { c = jsonParse(b); if (typeof c != \"object\") { return null } if (c.constructor === Array) { return null } return c } catch (a) { return null } }; KJUR.jws.JWS.getEncodedSignatureValueFromJWS = function (b) { var a = b.match(/^[^.]+\\.[^.]+\\.([^.]+)$/); if (a == null) { throw \"JWS signature is not a form of 'Head.Payload.SigValue'.\" } return a[1] }; KJUR.jws.JWS.getJWKthumbprint = function (d) { if (d.kty !== \"RSA\" && d.kty !== \"EC\" && d.kty !== \"oct\") { throw \"unsupported algorithm for JWK Thumprint\" } var a = \"{\"; if (d.kty === \"RSA\") { if (typeof d.n != \"string\" || typeof d.e != \"string\") { throw \"wrong n and e value for RSA key\" } a += '\"e\":\"' + d.e + '\",'; a += '\"kty\":\"' + d.kty + '\",'; a += '\"n\":\"' + d.n + '\"}' } else { if (d.kty === \"EC\") { if (typeof d.crv != \"string\" || typeof d.x != \"string\" || typeof d.y != \"string\") { throw \"wrong crv, x and y value for EC key\" } a += '\"crv\":\"' + d.crv + '\",'; a += '\"kty\":\"' + d.kty + '\",'; a += '\"x\":\"' + d.x + '\",'; a += '\"y\":\"' + d.y + '\"}' } else { if (d.kty === \"oct\") { if (typeof d.k != \"string\") { throw \"wrong k value for oct(symmetric) key\" } a += '\"kty\":\"' + d.kty + '\",'; a += '\"k\":\"' + d.k + '\"}' } } } var b = rstrtohex(a); var c = KJUR.crypto.Util.hashHex(b, \"sha256\"); var e = hextob64u(c); return e }; KJUR.jws.IntDate = {}; KJUR.jws.IntDate.get = function (c) { var b = KJUR.jws.IntDate, d = b.getNow, a = b.getZulu; if (c == \"now\") { return d() } else { if (c == \"now + 1hour\") { return d() + 60 * 60 } else { if (c == \"now + 1day\") { return d() + 60 * 60 * 24 } else { if (c == \"now + 1month\") { return d() + 60 * 60 * 24 * 30 } else { if (c == \"now + 1year\") { return d() + 60 * 60 * 24 * 365 } else { if (c.match(/Z$/)) { return a(c) } else { if (c.match(/^[0-9]+$/)) { return parseInt(c) } } } } } } } throw \"unsupported format: \" + c }; KJUR.jws.IntDate.getZulu = function (a) { return zulutosec(a) }; KJUR.jws.IntDate.getNow = function () { var a = ~~(new Date() / 1000); return a }; KJUR.jws.IntDate.intDate2UTCString = function (a) { var b = new Date(a * 1000); return b.toUTCString() }; KJUR.jws.IntDate.intDate2Zulu = function (e) { var i = new Date(e * 1000), h = (\"0000\" + i.getUTCFullYear()).slice(-4), g = (\"00\" + (i.getUTCMonth() + 1)).slice(-2), b = (\"00\" + i.getUTCDate()).slice(-2), a = (\"00\" + i.getUTCHours()).slice(-2), c = (\"00\" + i.getUTCMinutes()).slice(-2), f = (\"00\" + i.getUTCSeconds()).slice(-2); return h + g + b + a + c + f + \"Z\" };\nif (typeof KJUR == \"undefined\" || !KJUR) { KJUR = {} } if (typeof KJUR.jws == \"undefined\" || !KJUR.jws) { KJUR.jws = {} } KJUR.jws.JWSJS = function () { var c = KJUR, b = c.jws, a = b.JWS, d = a.readSafeJSONString; this.aHeader = []; this.sPayload = \"\"; this.aSignature = []; this.init = function () { this.aHeader = []; this.sPayload = undefined; this.aSignature = [] }; this.initWithJWS = function (f) { this.init(); var e = f.split(\".\"); if (e.length != 3) { throw \"malformed input JWS\" } this.aHeader.push(e[0]); this.sPayload = e[1]; this.aSignature.push(e[2]) }; this.addSignature = function (e, h, m, k) { if (this.sPayload === undefined || this.sPayload === null) { throw \"there's no JSON-JS signature to add.\" } var l = this.aHeader.length; if (this.aHeader.length != this.aSignature.length) { throw \"aHeader.length != aSignature.length\" } try { var f = KJUR.jws.JWS.sign(e, h, this.sPayload, m, k); var j = f.split(\".\"); var n = j[0]; var g = j[2]; this.aHeader.push(j[0]); this.aSignature.push(j[2]) } catch (i) { if (this.aHeader.length > l) { this.aHeader.pop() } if (this.aSignature.length > l) { this.aSignature.pop() } throw \"addSignature failed: \" + i } }; this.verifyAll = function (h) { if (this.aHeader.length !== h.length || this.aSignature.length !== h.length) { return false } for (var g = 0; g < h.length; g++) { var f = h[g]; if (f.length !== 2) { return false } var e = this.verifyNth(g, f[0], f[1]); if (e === false) { return false } } return true }; this.verifyNth = function (f, j, g) { if (this.aHeader.length <= f || this.aSignature.length <= f) { return false } var h = this.aHeader[f]; var k = this.aSignature[f]; var l = h + \".\" + this.sPayload + \".\" + k; var e = false; try { e = a.verify(l, j, g) } catch (i) { return false } return e }; this.readJWSJS = function (g) { if (typeof g === \"string\") { var f = d(g); if (f == null) { throw \"argument is not safe JSON object string\" } this.aHeader = f.headers; this.sPayload = f.payload; this.aSignature = f.signatures } else { try { if (g.headers.length > 0) { this.aHeader = g.headers } else { throw \"malformed header\" } if (typeof g.payload === \"string\") { this.sPayload = g.payload } else { throw \"malformed signatures\" } if (g.signatures.length > 0) { this.signatures = g.signatures } else { throw \"malformed signatures\" } } catch (e) { throw \"malformed JWS-JS JSON object: \" + e } } }; this.getJSON = function () { return { headers: this.aHeader, payload: this.sPayload, signatures: this.aSignature } }; this.isEmpty = function () { if (this.aHeader.length == 0) { return 1 } return 0 } };"
  },
  {
    "path": "ury/public/js/pos_extend.js",
    "content": "frappe.provide(\"erpnext.PointOfSale\");\nfrappe.pages[\"point-of-sale\"].on_page_load = function (wrapper) {\n  frappe.ui.make_app_page({\n    parent: wrapper,\n    title: __(\"Point of Sale\"),\n    single_column: true,\n  });\n\n  frappe.require(\"point-of-sale.bundle.js\", function () {\n    erpnext.PointOfSale.PastOrderList = class MyPastOrder extends (\n      erpnext.PointOfSale.PastOrderList\n    ) {\n      constructor(wrapper) {\n        super(wrapper);\n      }\n\n      make_filter_section() {\n        const me = this;\n        this.search_field = frappe.ui.form.make_control({\n          df: {\n            label: ('Search'),\n            fieldtype: 'Data',\n            placeholder:('Search by invoice id or customer name')\n          },\n          parent: this.$component.find(\".search-field\"),\n          render_input: true,\n        });\n\n        this.status_field = frappe.ui.form.make_control({\n          df: {\n            label: ('Invoice Status'),\n            fieldtype: 'Select',\n            options: `Draft\\nTo Bill`,\n            placeholder: ('Filter by invoice status'),\n            onchange: function () {\n              if (me.$component.is(\":visible\")) me.refresh_list();\n            },\n          },\n          parent: this.$component.find(\".status-field\"),\n          render_input: true,\n        });\n        this.search_field.toggle_label(false);\n        this.status_field.toggle_label(false);\n        this.status_field.set_value('Draft');\n      }\n      refresh_list() {\n        frappe.dom.freeze();\n        this.events.reset_summary();\n        const search_term = this.search_field.get_value();\n        const status = this.status_field.get_value();\n\n        this.$invoices_container.html(\"\");\n\n        return frappe.call({\n          method: \"ury.ury.api.pos_extend.overrided_past_order_list\",\n          freeze: true,\n          args: { search_term, status },\n          callback: (response) => {\n            frappe.dom.unfreeze();\n            response.message.forEach((invoice) => {\n              const invoice_html = this.get_invoice_html(invoice);\n              this.$invoices_container.append(invoice_html);\n            });\n          },\n        });\n      }\n    };\n\n    erpnext.PointOfSale.Controller = class MyPosController extends (\n      erpnext.PointOfSale.Controller\n    ) {\n      constructor(wrapper) {\n        super(wrapper);\n      }\n      prepare_menu() {\n        this.page.clear_menu();\n\n        this.page.add_menu_item(\n          __(\"Toggle Recent Orders\"),\n          this.toggle_recent_order.bind(this),\n          false,\n          \"Ctrl+O\"\n        );\n\n        this.page.add_menu_item(\n          __(\"Cancel Order\"),\n          this.cancel_order.bind(this),\n          false,\n          \"Ctrl+I\"\n        );\n      }\n      cancel_order() {\n        if (!this.$components_wrapper.is(\":visible\")) return;\n\n        if (this.frm.doc.name.startsWith(\"new-pos\")) {\n            frappe.show_alert({\n                message: __(\"You must save document as draft to cancel.\"),\n                indicator: 'red'\n            });\n            frappe.utils.play_sound(\"error\");\n            return;\n        }\n        if (this.frm.doc.restaurant_table) {\n            frappe.throw({\n                message: __(\"Not allowed to cancel table orders through PoS\")\n            });\n        }\n        else {\n            if (this.frm.doc.invoice_printed == 1) {\n                frappe.throw({\n                    title: __(\"Invoice Already Billed\"),\n                    message: __(\"Not allowed to cancel billed orders.\"),\n                    indicator: 'red'\n                });\n            }\n            else {\n                let cancel_flag = false;\n                var dialog = new frappe.ui.Dialog({\n                    title: __(\"Confirm Cancellation\"),\n                    fields: [\n                        {\n                            fieldname: 'reason',\n                            fieldtype: 'Data',\n                            label: __('Reason'),\n                            reqd: 1\n                        }\n                    ],\n                    primary_action: function () {\n                        var reason = dialog.get_value('reason');\n                        if (!cancel_flag) {\n                            cancel_flag = true;\n                            this.frm.reason = reason;\n                            this.frm.cancel_reason = reason;\n                            this.cancel(this.frm.cancel_reason);\n                            dialog.hide();\n                        }\n                    }.bind(this),\n                    primary_action_label: __('Cancel'),\n                });\n                dialog.show();\n            }\n        }\n\n    }\n    cancel() {\n\n        frappe.call({\n            method: 'ury.ury.doctype.ury_order.ury_order.cancel_order',\n            args: {\n                invoice_id: this.frm.doc.name,\n                reason: this.frm.cancel_reason\n            },\n            callback: function (r) {\n                frappe.show_alert({ message: __('Cancelled'), indicator: 'red' });\n                setTimeout(function () {\n                    window.location.reload();\n                }, 1000)\n            }\n        });\n    }\n    };\n\n    erpnext.PointOfSale.PastOrderList = class MyPastOrderList extends (\n      erpnext.PointOfSale.PastOrderList\n    ) {\n      constructor(wrapper) {\n        super(wrapper);\n      }\n      get_invoice_html(invoice) {\n        const posting_datetime = moment(\n          invoice.posting_date + \" \" + invoice.posting_time\n        ).format(\"Do MMMM, h:mma\");\n        return `<div class=\"invoice-wrapper\" data-invoice-name=\"${escape(\n          invoice.name\n        )}\">\n\t\t\t\t\t\t<div class=\"invoice-name-date\">\n\t\t\t\t\t\t\t<div class=\"invoice-name\">${invoice.name}</div>\n\t\t\t\t\t\t\t<div class=\"invoice-date\">\n\t\t\t\t\t\t\t\t<svg class=\"mr-2\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n\t\t\t\t\t\t\t\t\t<path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t${frappe.ellipsis(invoice.customer, 20)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"invoice-table\" style=\"display:flex; text-align:center; align-items: center; font-weight: 600; font-size: 14px;\">${\n              invoice.restaurant_table ? invoice.restaurant_table : \"\"\n            }</div>\n\t\t\t\t\t\t<div class=\"invoice-total-status\">\n\t\t\t\t\t\t\t<div class=\"invoice-total\">${\n                format_currency(invoice.grand_total, invoice.currency, 0) || 0\n              }</div>\n\t\t\t\t\t\t\t<div class=\"invoice-date\">${posting_datetime}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"seperator\"></div>`;\n      }\n    };\n\n    erpnext.PointOfSale.PastOrderSummary = class MyPastOrderSummary extends (\n      erpnext.PointOfSale.PastOrderSummary\n    ) {\n      constructor(wrapper) {\n        super(wrapper);\n      }\n      bind_events() {\n        this.$summary_container.on(\"click\", \".return-btn\", () => {\n          this.events.process_return(this.doc.name);\n          this.toggle_component(false);\n          this.$component\n            .find(\".no-summary-placeholder\")\n            .css(\"display\", \"flex\");\n          this.$summary_wrapper.css(\"display\", \"none\");\n        });\n\n        this.$summary_container.on(\"click\", \".edit-btn\", () => {\n          var addCommentWrapper = document.querySelector(\n            \".add-comment-wrapper\"\n          );\n          addCommentWrapper.style.display = \"flex\";\n          this.events.edit_order(this.doc.name);\n          // this.check();\n          this.toggle_component(false);\n          this.$component\n            .find(\".no-summary-placeholder\")\n            .css(\"display\", \"flex\");\n          this.$summary_wrapper.css(\"display\", \"none\");\n\n          let items = this.doc.items;\n          cur_frm.old_items = [];\n\n          items.forEach((old_item) => {\n            let json_olditem = {\n              item_code: old_item.item_code,\n              qty: old_item.qty,\n              item_name: old_item.item_name,\n              name: old_item.name,\n            };\n            cur_frm.old_items.push(json_olditem);\n          });\n          cur_frm.check = true;\n        });\n\n        this.$summary_container.on(\"click\", \".delete-btn\", () => {\n          this.events.delete_order(this.doc.name);\n          this.show_summary_placeholder();\n        });\n\n        this.$summary_container.on(\"click\", \".delete-btn\", () => {\n          this.events.delete_order(this.doc.name);\n          this.show_summary_placeholder();\n          // this.toggle_component(false);\n          // this.$component.find('.no-summary-placeholder').removeClass('d-none');\n          // this.$summary_wrapper.addClass('d-none');\n        });\n\n        this.$summary_container.on(\"click\", \".new-btn\", () => {\n          var addCommentWrapper = document.querySelector(\n            \".add-comment-wrapper\"\n          );\n          addCommentWrapper.style.display = \"flex\";\n\n          this.events.new_order();\n          this.toggle_component(false);\n          this.$component\n            .find(\".no-summary-placeholder\")\n            .css(\"display\", \"flex\");\n          this.$summary_wrapper.css(\"display\", \"none\");\n          cur_frm.check = true;\n          cur_frm.old_items = [];\n        });\n\n        this.$summary_container.on(\"click\", \".email-btn\", () => {\n          this.email_dialog.fields_dict.email_id.set_value(this.customer_email);\n          this.email_dialog.show();\n        });\n\n        this.$summary_container.on(\"click\", \".print-btn\", () => {\n          this.print_receipt();\n        });\n      }\n    };\n    erpnext.PointOfSale.ItemCart = class MyItemCart extends (\n      erpnext.PointOfSale.ItemCart\n    ) {\n      constructor(wrapper) {\n        super(wrapper);\n      }\n      init_cart_components() {\n        this.$component.append(\n          `<div class=\"cart-container\">\n\t\t\t\t\t\t<div class=\"abs-cart-container\">\n\t\t\t\t\t\t\t<div class=\"cart-label\">${__(\"Item Cart\")}</div>\n\t\t\t\t\t\t\t<div class=\"cart-header\">\n\t\t\t\t\t\t\t\t<div class=\"name-header\">${__(\"Item\")}</div>\n\t\t\t\t\t\t\t\t<div class=\"qty-header\">${__(\"Quantity\")}</div>\n\t\t\t\t\t\t\t\t<div class=\"rate-amount-header\">${__(\"Amount\")}</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"cart-items-section\"></div>\n\t\t\t\t\t\t\t<div class=\"cart-totals-section\"></div>\n\t\t\t\t\t\t\t<div class=\"numpad-section\"></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>`\n        );\n        this.$cart_container = this.$component.find(\".cart-container\");\n\n        this.make_cart_totals_section();\n        this.make_cart_items_section();\n        this.make_cart_numpad();\n        const commentButton = this.$component.find(\n          \".add-comment-wrapper button\"\n        );\n        commentButton.on(\"click\", () => {\n          let d = new frappe.ui.Dialog({\n            title: \"Enter Comment\",\n            fields: [\n              {\n                label: \"Comment\",\n                fieldname: \"comment\",\n                fieldtype: \"Data\",\n                default: cur_frm.order_comments,\n              },\n            ],\n            primary_action_label: \"Add\",\n            primary_action: (values) => {\n              cur_frm.order_comments = values.comment;\n              d.hide();\n            },\n          });\n          d.show();\n          $(document).on(\"shown.bs.modal\", \".modal\", function () {\n            let modal = $(this);\n            modal.find('input[data-fieldname=\"comment\"]').focus();\n          });\n        });\n      }\n      make_cart_totals_section() {\n        this.$totals_section = this.$component.find(\".cart-totals-section\");\n\n        this.$totals_section.append(\n          `<div class=\"add-comment-wrapper\" style=\"display: flex; justify-content: center; align-items: center;\">\n\t\t\t\t\t\t<button class=\"btn btn-primary btn-sm primary-action\" style=\"width: 300px; height: 40px; font-size: 16px; background-color: #428bca; color: #fff; border: none; border-radius: 4px;\">Add Comment</button>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"add-discount-wrapper\">\n\t\t\t\t\t\t${this.get_discount_icon()} ${__(\"Add Discount\")}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"item-qty-total-container\">\n\t\t\t\t\t\t<div class=\"item-qty-total-label\">${__(\"Total Items\")}</div>\n\t\t\t\t\t\t<div class=\"item-qty-total-value\">0.00</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"net-total-container\">\n\t\t\t\t\t\t<div class=\"net-total-label\">${__(\"Net Total\")}</div>\n\t\t\t\t\t\t<div class=\"net-total-value\">0.00</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"taxes-container\"></div>\n\t\t\t\t\t<div class=\"grand-total-container\">\n\t\t\t\t\t\t<div>${__(\"Grand Total\")}</div>\n\t\t\t\t\t\t<div>0.00</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"checkout-btn\">${__(\"Checkout\")}</div>\n\t\t\t\t\t<div class=\"edit-cart-btn\">${__(\"Edit Cart\")}</div>`\n        );\n\n        this.$add_discount_elem = this.$component.find(\".add-discount-wrapper\");\n      }\n      toggle_checkout_btn(show_checkout) {\n        if (show_checkout) {\n          this.$totals_section\n            .find(\".add-comment-wrapper\")\n            .css(\"display\", \"flex\");\n          this.$totals_section.find(\".checkout-btn\").css(\"display\", \"flex\");\n          this.$totals_section.find(\".edit-cart-btn\").css(\"display\", \"none\");\n        } else {\n          this.$totals_section\n            .find(\".add-comment-wrapper\")\n            .css(\"display\", \"none\");\n          this.$totals_section.find(\".checkout-btn\").css(\"display\", \"none\");\n          this.$totals_section.find(\".edit-cart-btn\").css(\"display\", \"flex\");\n        }\n      }\n    };\n\n    wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);\n    window.cur_pos = wrapper.pos;\n  });\n};\n"
  },
  {
    "path": "ury/public/js/pos_print.js",
    "content": "frappe.require([\n    '/assets/ury/js/qz-tray.js',\n    '/assets/ury/js/jsrsasign-all-min.js',\n    '/assets/ury/js/sign-message.js'\n]);\n\nfrappe.ui.form.on('POS Invoice', {\n\n    before_save: function (frm) {\n        if (frm.doc.customer_name === null || frm.doc.customer_name === \"\") {\n            frappe.throw({\n                message: __(\"Failed to load data . Please refresh the page\")\n            });\n        }\n    },\n\n    print: function (frm) {\n        let invoice = frm.doc.name\n        frappe.db.get_doc('POS Invoice', invoice).then(pos_invoice => {\n            if (pos_invoice.invoice_printed == 1) {\n                frappe.throw({\n                    title: __(\"Invoice Already Billed\"),\n                    message: __(\"This order has already been billed. Please reload the page.\"),\n                    indicator: 'red'\n                });\n            }\n            frappe.dom.freeze(__('Printing Invoice'));\n            frappe.db.get_doc('POS Profile', frm.doc.pos_profile).then(profile => {\n\n                if (profile.qz_print == 1) {\n                    // To fetch qz_key from site config\n                    frappe.call({\n                        method: \"ury.ury.api.ury_print.qz_certificate\",\n                        callback: function (response) {\n                            if (response.message) {\n                                var qzKey = response.message;\n                                qz.security.setCertificatePromise(function (resolve, reject) {\n                                    //Preferred method - from server\n                                    fetch(\"/private/\" + qzKey, { cache: 'no-store', headers: { 'Content-Type': 'text/plain' } })\n                                        .then(function (data) { data.ok ? resolve(data.text()) : reject(data.text()); });\n                                });\n\n                            }\n                        }\n                    });\n\n                    frappe.call({\n                        method: \"frappe.www.printview.get_html_and_style\",\n                        args: {\n                            doc: \"POS Invoice\",\n                            name: invoice,\n                            print_format: profile.print_format,\n                            _lang: 'en',\n                        },\n                        callback: function (r) {\n\n                            if (qz.websocket.isActive()) {\n                                // Use the existing connection to print\n                                printWithQZTray();\n                            } else {\n                                // Establish a new connection and then print\n                                qz.websocket.connect({ host: profile.qz_host })\n                                    .then(() => {\n                                        // test();\n                                        printWithQZTray();\n                                    })\n                                    .catch((error) => {\n                                        // Handle connection error\n                                        console.error(\"Error connecting to QZ Tray:\", error);\n                                        frappe.dom.unfreeze();\n                                        frappe.throw({\n                                            message: __(\"Printing Failed: Error connecting to QZ Tray\")\n                                        });\n                                    });\n                            }\n                            function printWithQZTray() {\n                                qz.printers.getDefault()\n                                    .then((printer) => {\n                                        var htmlcontent = r.message.html;\n                                        var data = [{\n                                            type: 'html',\n                                            format: 'plain',\n                                            data: htmlcontent\n                                        }];\n\n                                        var config = qz.configs.create(printer);\n                                        qz.print(config, data)\n                                            .then(function () {\n                                                frappe.call({\n                                                    method: `ury.ury.api.ury_print.qz_print_update`,\n                                                    args: {\n                                                        invoice: invoice\n                                                    },\n                                                    callback: function (r) {\n                                                    }\n                                                });\n                                                cur_frm.set_value('invoice_printed', 1);\n                                                frappe.dom.unfreeze();\n                                                frappe.show_alert({ message: __('Invoice Printed'), indicator: 'green' });\n                                            })\n                                            .catch(function (error) {\n                                                // Handle printing error\n                                                console.error(\"Error printing with QZ Tray:\", error);\n                                                frappe.dom.unfreeze();\n                                                frappe.throw({\n                                                    message: __(\"Printing Failed: Error printing with QZ Tray\")\n                                                });\n                                            });\n                                    })\n                                    .catch(function (error) {\n                                        // Handle printer lookup error\n                                        console.error(\"Error looking up printer:\", error);\n                                        frappe.dom.unfreeze();\n                                        frappe.throw({\n                                            message: __(\"Printing Failed: Error looking up printer\")\n                                        });\n                                    });\n                            }\n                        },\n                    });\n                }\n                else if (profile.printer_settings.some(e => e.bill == 1)) {\n                    profile.printer_settings.forEach(print => {\n                        frappe.call({\n                            method: `ury.ury.api.ury_print.network_printing`,\n                            args: {\n                                doctype: \"POS Invoice\",\n                                name: invoice,\n                                printer_setting: print.printer,\n                                print_format: profile.print_format\n                            },\n                            callback: function (r) {\n                                if (r.message == \"Success\") {\n                                    $('.standard-actions').addClass('hidden-xs hidden-md');\n                                    frappe.show_alert({ message: __('Invoice Printed'), indicator: 'green' });\n                                    cur_frm.set_value('invoice_printed', 1);\n                                    frappe.dom.unfreeze();\n                                    cur_frm.reload_doc();\n                                }\n                                else {\n                                    console.error(r.message);\n                                    frappe.dom.unfreeze();\n                                    frappe.throw({\n                                        message: __(\"Printing Failed\")\n                                    });\n                                }\n                            },\n                            error: function (xhr, textStatus, error) {\n                                console.error(\"AJAX Error:\", error); // Log the AJAX error\n                                frappe.dom.unfreeze();\n                                frappe.throw({\n                                    message: __(\"An error occurred while printing\")\n                                });\n                            }\n                        })\n                    })\n                }\n                else {\n                    frappe.call({\n                        method: `ury.ury.api.ury_print.print_pos_page`,\n                        args: {\n                            doctype: \"POS Invoice\",\n                            name: invoice,\n                            print_format: profile.print_format\n                        },\n                        callback: function (r) {\n                            $('.standard-actions').addClass('hidden-xs hidden-md');\n                            cur_frm.set_value('invoice_printed', 1);\n                            frappe.show_alert({ message: __('Invoice Printed'), indicator: 'green' });\n                            frappe.ui.toolbar.clear_cache()\n                            frappe.dom.unfreeze();\n                        }\n                    });\n                }\n            })\n        })\n    }\n\n})\n\n"
  },
  {
    "path": "ury/public/js/quick_entry.js",
    "content": "frappe.provide('frappe.ui.form');\nfrappe.ui.form.CustomerQuickEntryForm = class POSQuickEntryForm extends frappe.ui.form.QuickEntryForm {\n    constructor(doctype, after_insert, init_callback, doc, force) {\n        super(doctype, after_insert, init_callback, doc, force);\n        this.skip_redirect_on_error = true;\n    }\n\n\n    render_dialog() {\n\n        this.mandatory = this.getfields();\n        super.render_dialog();\n    }\n\n    getfields() {\n\n        var variant_fields = [\n            {\n                label: __(\"Customer Name\"),\n                fieldname: \"customer_name\",\n                fieldtype: \"Data\"\n            },\n            {\n                label: __(\"Mobile Number\"),\n                fieldname: \"mobile_number\",\n                fieldtype: \"Int\"\n            },\n            {\n                label: __(\"Customer Group\"),\n                fieldname: \"customer_group\",\n                fieldtype: \"Link\"\n            },\n            {\n                label: __(\"Customer Territory\"),\n                fieldname: \"territory\",\n                fieldtype: \"Link\"\n            }\n        ];\n\n        return variant_fields;\n    }\n}\n"
  },
  {
    "path": "ury/public/js/qz-tray.js",
    "content": "'use strict';\n\n/**\n * @version 2.2.3-SNAPSHOT\n * @overview QZ Tray Connector\n * <p/>\n * Connects a web client to the QZ Tray software.\n * Enables printing and device communication from javascript.\n */\nvar qz = (function () {\n\n    ///// POLYFILLS /////\n\n    if (!Array.isArray) {\n        Array.isArray = function (arg) {\n            return Object.prototype.toString.call(arg) === '[object Array]';\n        };\n    }\n\n    if (!Number.isInteger) {\n        Number.isInteger = function (value) {\n            return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;\n        };\n    }\n\n    ///// PRIVATE METHODS /////\n\n    var _qz = {\n        VERSION: \"2.2.3-SNAPSHOT\",                              //must match @version above\n        DEBUG: false,\n\n        log: {\n            /** Debugging messages */\n            trace: function () { if (_qz.DEBUG) { console.log.apply(console, arguments); } },\n            /** General messages */\n            info: function () { console.info.apply(console, arguments); },\n            /** General warnings */\n            warn: function () { console.warn.apply(console, arguments); },\n            /** Debugging errors */\n            allay: function () { if (_qz.DEBUG) { console.warn.apply(console, arguments); } },\n            /** General errors */\n            error: function () { console.error.apply(console, arguments); }\n        },\n\n\n        //stream types\n        streams: {\n            serial: 'SERIAL', usb: 'USB', hid: 'HID', printer: 'PRINTER', file: 'FILE', socket: 'SOCKET'\n        },\n\n\n        websocket: {\n            /** The actual websocket object managing the connection. */\n            connection: null,\n\n            /** Default parameters used on new connections. Override values using options parameter on {@link qz.websocket.connect}. */\n            connectConfig: {\n                host: [\"localhost\", \"localhost.qz.io\"], //hosts QZ Tray can be running on\n                hostIndex: 0,                           //internal var - index on host array\n                usingSecure: true,                      //boolean use of secure protocol\n                protocol: {\n                    secure: \"wss://\",                   //secure websocket\n                    insecure: \"ws://\"                   //insecure websocket\n                },\n                port: {\n                    secure: [8181, 8282, 8383, 8484],   //list of secure ports QZ Tray could be listening on\n                    insecure: [8182, 8283, 8384, 8485], //list of insecure ports QZ Tray could be listening on\n                    portIndex: 0                        //internal var - index on active port array\n                },\n                keepAlive: 60,                          //time between pings to keep connection alive, in seconds\n                retries: 0,                             //number of times to reconnect before failing\n                delay: 0                                //seconds before firing a connection\n            },\n\n            setup: {\n                /** Loop through possible ports to open connection, sets web socket calls that will settle the promise. */\n                findConnection: function (config, resolve, reject) {\n                    //force flag if missing ports\n                    if (!config.port.secure.length) {\n                        if (!config.port.insecure.length) {\n                            reject(new Error(\"No ports have been specified to connect over\"));\n                            return;\n                        } else if (config.usingSecure) {\n                            _qz.log.error(\"No secure ports specified - forcing insecure connection\");\n                            config.usingSecure = false;\n                        }\n                    } else if (!config.port.insecure.length && !config.usingSecure) {\n                        _qz.log.trace(\"No insecure ports specified - forcing secure connection\");\n                        config.usingSecure = true;\n                    }\n\n                    var deeper = function () {\n                        config.port.portIndex++;\n\n                        if ((config.usingSecure && config.port.portIndex >= config.port.secure.length)\n                            || (!config.usingSecure && config.port.portIndex >= config.port.insecure.length)) {\n                            if (config.hostIndex >= config.host.length - 1) {\n                                //give up, all hope is lost\n                                reject(new Error(\"Unable to establish connection with QZ\"));\n                                return;\n                            } else {\n                                config.hostIndex++;\n                                config.port.portIndex = 0;\n                            }\n                        }\n\n                        // recursive call until connection established or all ports are exhausted\n                        _qz.websocket.setup.findConnection(config, resolve, reject);\n                    };\n\n                    var address;\n                    if (config.usingSecure) {\n                        address = config.protocol.secure + config.host[config.hostIndex] + \":\" + config.port.secure[config.port.portIndex];\n                    } else {\n                        address = config.protocol.insecure + config.host[config.hostIndex] + \":\" + config.port.insecure[config.port.portIndex];\n                    }\n\n                    try {\n                        _qz.log.trace(\"Attempting connection\", address);\n                        _qz.websocket.connection = new _qz.tools.ws(address);\n                    }\n                    catch (err) {\n                        _qz.log.error(err);\n                        deeper();\n                        return;\n                    }\n\n                    if (_qz.websocket.connection != null) {\n                        _qz.websocket.connection.established = false;\n\n                        //called on successful connection to qz, begins setup of websocket calls and resolves connect promise after certificate is sent\n                        _qz.websocket.connection.onopen = function (evt) {\n                            if (!_qz.websocket.connection.established) {\n                                _qz.log.trace(evt);\n                                _qz.log.info(\"Established connection with QZ Tray on \" + address);\n\n                                _qz.websocket.setup.openConnection({ resolve: resolve, reject: reject });\n\n                                if (config.keepAlive > 0) {\n                                    var interval = setInterval(function () {\n                                        if (!_qz.tools.isActive() || _qz.websocket.connection.interval !== interval) {\n                                            clearInterval(interval);\n                                            return;\n                                        }\n\n                                        _qz.websocket.connection.send(\"ping\");\n                                    }, config.keepAlive * 1000);\n\n                                    _qz.websocket.connection.interval = interval;\n                                }\n                            }\n                        };\n\n                        //called during websocket close during setup\n                        _qz.websocket.connection.onclose = function () {\n                            // Safari compatibility fix to raise error event\n                            if (_qz.websocket.connection && typeof navigator !== 'undefined' && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {\n                                _qz.websocket.connection.onerror();\n                            }\n                        };\n\n                        //called for errors during setup (such as invalid ports), reject connect promise only if all ports have been tried\n                        _qz.websocket.connection.onerror = function (evt) {\n                            _qz.log.trace(evt);\n\n                            _qz.websocket.connection = null;\n\n                            deeper();\n                        };\n                    } else {\n                        reject(new Error(\"Unable to create a websocket connection\"));\n                    }\n                },\n\n                /** Finish setting calls on successful connection, sets web socket calls that won't settle the promise. */\n                openConnection: function (openPromise) {\n                    _qz.websocket.connection.established = true;\n\n                    //called when an open connection is closed\n                    _qz.websocket.connection.onclose = function (evt) {\n                        _qz.log.trace(evt);\n\n                        _qz.websocket.connection = null;\n                        _qz.websocket.callClose(evt);\n                        _qz.log.info(\"Closed connection with QZ Tray\");\n\n                        for (var uid in _qz.websocket.pendingCalls) {\n                            if (_qz.websocket.pendingCalls.hasOwnProperty(uid)) {\n                                _qz.websocket.pendingCalls[uid].reject(new Error(\"Connection closed before response received\"));\n                            }\n                        }\n\n                        //if this is set, then an explicit close call was made\n                        if (this.promise != undefined) {\n                            this.promise.resolve();\n                        }\n                    };\n\n                    //called for any errors with an open connection\n                    _qz.websocket.connection.onerror = function (evt) {\n                        _qz.websocket.callError(evt);\n                    };\n\n                    //send JSON objects to qz\n                    _qz.websocket.connection.sendData = function (obj) {\n                        _qz.log.trace(\"Preparing object for websocket\", obj);\n\n                        if (obj.timestamp == undefined) {\n                            obj.timestamp = Date.now();\n                            if (typeof obj.timestamp !== 'number') {\n                                obj.timestamp = new Date().getTime();\n                            }\n                        }\n                        if (obj.promise != undefined) {\n                            obj.uid = _qz.websocket.setup.newUID();\n                            _qz.websocket.pendingCalls[obj.uid] = obj.promise;\n                        }\n\n                        // track requesting monitor\n                        obj.position = {\n                            x: typeof screen !== 'undefined' ? ((screen.availWidth || screen.width) / 2) + (screen.left || screen.availLeft || 0) : 0,\n                            y: typeof screen !== 'undefined' ? ((screen.availHeight || screen.height) / 2) + (screen.top || screen.availTop || 0) : 0\n                        };\n\n                        try {\n                            if (obj.call != undefined && obj.signature == undefined && _qz.security.needsSigned(obj.call)) {\n                                var signObj = {\n                                    call: obj.call,\n                                    params: obj.params,\n                                    timestamp: obj.timestamp\n                                };\n\n                                //make a hashing promise if not already one\n                                var hashing = _qz.tools.hash(_qz.tools.stringify(signObj));\n                                if (!hashing.then) {\n                                    hashing = _qz.tools.promise(function (resolve) {\n                                        resolve(hashing);\n                                    });\n                                }\n\n                                hashing.then(function (hashed) {\n                                    return _qz.security.callSign(hashed);\n                                }).then(function (signature) {\n                                    _qz.log.trace(\"Signature for call\", signature);\n                                    obj.signature = signature || \"\";\n                                    obj.signAlgorithm = _qz.security.signAlgorithm;\n\n                                    _qz.signContent = undefined;\n                                    _qz.websocket.connection.send(_qz.tools.stringify(obj));\n                                });\n                            } else {\n                                _qz.log.trace(\"Signature for call\", obj.signature);\n\n                                //called for pre-signed content and (unsigned) setup calls\n                                _qz.websocket.connection.send(_qz.tools.stringify(obj));\n                            }\n                        }\n                        catch (err) {\n                            _qz.log.error(err);\n\n                            if (obj.promise != undefined) {\n                                obj.promise.reject(err);\n                                delete _qz.websocket.pendingCalls[obj.uid];\n                            }\n                        }\n                    };\n\n                    //receive message from qz\n                    _qz.websocket.connection.onmessage = function (evt) {\n                        var returned = JSON.parse(evt.data);\n\n                        if (returned.uid == null) {\n                            if (returned.type == null) {\n                                //incorrect response format, likely connected to incompatible qz version\n                                _qz.websocket.connection.close(4003, \"Connected to incompatible QZ Tray version\");\n\n                            } else {\n                                //streams (callbacks only, no promises)\n                                switch (returned.type) {\n                                    case _qz.streams.serial:\n                                        if (!returned.event) {\n                                            returned.event = JSON.stringify({ portName: returned.key, output: returned.data });\n                                        }\n\n                                        _qz.serial.callSerial(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.socket:\n                                        _qz.socket.callSocket(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.usb:\n                                        if (!returned.event) {\n                                            returned.event = JSON.stringify({ vendorId: returned.key[0], productId: returned.key[1], output: returned.data });\n                                        }\n\n                                        _qz.usb.callUsb(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.hid:\n                                        _qz.hid.callHid(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.printer:\n                                        _qz.printers.callPrinter(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.file:\n                                        _qz.file.callFile(JSON.parse(returned.event));\n                                        break;\n                                    default:\n                                        _qz.log.allay(\"Cannot determine stream type for callback\", returned);\n                                        break;\n                                }\n                            }\n\n                            return;\n                        }\n\n                        _qz.log.trace(\"Received response from websocket\", returned);\n\n                        var promise = _qz.websocket.pendingCalls[returned.uid];\n                        if (promise == undefined) {\n                            _qz.log.allay('No promise found for returned response');\n                        } else {\n                            if (returned.error != undefined) {\n                                promise.reject(new Error(returned.error));\n                            } else {\n                                promise.resolve(returned.result);\n                            }\n                        }\n\n                        delete _qz.websocket.pendingCalls[returned.uid];\n                    };\n\n\n                    //send up the certificate before making any calls\n                    //also gives the user a chance to deny the connection\n                    function sendCert(cert) {\n                        if (cert === undefined) { cert = null; }\n\n                        //websocket setup, query what version is connected\n                        qz.api.getVersion().then(function (version) {\n                            _qz.websocket.connection.version = version;\n                            _qz.websocket.connection.semver = version.toLowerCase().replace(/-rc\\./g, \"-rc\").split(/[\\\\+\\\\.-]/g);\n                            for (var i = 0; i < _qz.websocket.connection.semver.length; i++) {\n                                try {\n                                    if (i == 3 && _qz.websocket.connection.semver[i].toLowerCase().indexOf(\"rc\") == 0) {\n                                        // Handle \"rc1\" pre-release by negating build info\n                                        _qz.websocket.connection.semver[i] = -(_qz.websocket.connection.semver[i].replace(/\\D/g, \"\"));\n                                        continue;\n                                    }\n                                    _qz.websocket.connection.semver[i] = parseInt(_qz.websocket.connection.semver[i]);\n                                }\n                                catch (ignore) { }\n\n                                if (_qz.websocket.connection.semver.length < 4) {\n                                    _qz.websocket.connection.semver[3] = 0;\n                                }\n                            }\n\n                            //algorithm can be declared before a connection, check for incompatibilities now that we have one\n                            _qz.compatible.algorithm(true);\n                        }).then(function () {\n                            _qz.websocket.connection.sendData({ certificate: cert, promise: openPromise });\n                        });\n                    }\n\n                    _qz.security.callCert().then(sendCert).catch(function (error) {\n                        _qz.log.warn(\"Failed to get certificate:\", error);\n                        sendCert(null);\n                    });\n                },\n\n                /** Generate unique ID used to map a response to a call. */\n                newUID: function () {\n                    var len = 6;\n                    return (new Array(len + 1).join(\"0\") + (Math.random() * Math.pow(36, len) << 0).toString(36)).slice(-len)\n                }\n            },\n\n            dataPromise: function (callName, params, signature, signingTimestamp) {\n                return _qz.tools.promise(function (resolve, reject) {\n                    var msg = {\n                        call: callName,\n                        promise: { resolve: resolve, reject: reject },\n                        params: params,\n                        signature: signature,\n                        timestamp: signingTimestamp\n                    };\n\n                    _qz.websocket.connection.sendData(msg);\n                });\n            },\n\n            /** Library of promises awaiting a response, uid -> promise */\n            pendingCalls: {},\n\n            /** List of functions to call on error from the websocket. */\n            errorCallbacks: [],\n            /** Calls all functions registered to listen for errors. */\n            callError: function (evt) {\n                if (Array.isArray(_qz.websocket.errorCallbacks)) {\n                    for (var i = 0; i < _qz.websocket.errorCallbacks.length; i++) {\n                        _qz.websocket.errorCallbacks[i](evt);\n                    }\n                } else {\n                    _qz.websocket.errorCallbacks(evt);\n                }\n            },\n\n            /** List of function to call on closing from the websocket. */\n            closedCallbacks: [],\n            /** Calls all functions registered to listen for closing. */\n            callClose: function (evt) {\n                if (Array.isArray(_qz.websocket.closedCallbacks)) {\n                    for (var i = 0; i < _qz.websocket.closedCallbacks.length; i++) {\n                        _qz.websocket.closedCallbacks[i](evt);\n                    }\n                } else {\n                    _qz.websocket.closedCallbacks(evt);\n                }\n            }\n        },\n\n\n        printing: {\n            /** Default options used for new printer configs. Can be overridden using {@link qz.configs.setDefaults}. */\n            defaultConfig: {\n                //value purposes are explained in the qz.configs.setDefaults docs\n\n                bounds: null,\n                colorType: 'color',\n                copies: 1,\n                density: 0,\n                duplex: false,\n                fallbackDensity: null,\n                interpolation: 'bicubic',\n                jobName: null,\n                legacy: false,\n                margins: 0,\n                orientation: null,\n                paperThickness: null,\n                printerTray: null,\n                rasterize: false,\n                rotation: 0,\n                scaleContent: true,\n                size: null,\n                units: 'in',\n\n                forceRaw: false,\n                encoding: null,\n                spool: null\n            }\n        },\n\n\n        serial: {\n            /** List of functions called when receiving data from serial connection. */\n            serialCallbacks: [],\n            /** Calls all functions registered to listen for serial events. */\n            callSerial: function (streamEvent) {\n                if (Array.isArray(_qz.serial.serialCallbacks)) {\n                    for (var i = 0; i < _qz.serial.serialCallbacks.length; i++) {\n                        _qz.serial.serialCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.serial.serialCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        socket: {\n            /** List of functions called when receiving data from network socket connection. */\n            socketCallbacks: [],\n            /** Calls all functions registered to listen for network socket events. */\n            callSocket: function (socketEvent) {\n                if (Array.isArray(_qz.socket.socketCallbacks)) {\n                    for (var i = 0; i < _qz.socket.socketCallbacks.length; i++) {\n                        _qz.socket.socketCallbacks[i](socketEvent);\n                    }\n                } else {\n                    _qz.socket.socketCallbacks(socketEvent);\n                }\n            }\n        },\n\n\n        usb: {\n            /** List of functions called when receiving data from usb connection. */\n            usbCallbacks: [],\n            /** Calls all functions registered to listen for usb events. */\n            callUsb: function (streamEvent) {\n                if (Array.isArray(_qz.usb.usbCallbacks)) {\n                    for (var i = 0; i < _qz.usb.usbCallbacks.length; i++) {\n                        _qz.usb.usbCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.usb.usbCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        hid: {\n            /** List of functions called when receiving data from hid connection. */\n            hidCallbacks: [],\n            /** Calls all functions registered to listen for hid events. */\n            callHid: function (streamEvent) {\n                if (Array.isArray(_qz.hid.hidCallbacks)) {\n                    for (var i = 0; i < _qz.hid.hidCallbacks.length; i++) {\n                        _qz.hid.hidCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.hid.hidCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        printers: {\n            /** List of functions called when receiving data from printer connection. */\n            printerCallbacks: [],\n            /** Calls all functions registered to listen for printer events. */\n            callPrinter: function (streamEvent) {\n                if (Array.isArray(_qz.printers.printerCallbacks)) {\n                    for (var i = 0; i < _qz.printers.printerCallbacks.length; i++) {\n                        _qz.printers.printerCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.printers.printerCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        file: {\n            /** List of functions called when receiving info regarding file changes. */\n            fileCallbacks: [],\n            /** Calls all functions registered to listen for file events. */\n            callFile: function (streamEvent) {\n                if (Array.isArray(_qz.file.fileCallbacks)) {\n                    for (var i = 0; i < _qz.file.fileCallbacks.length; i++) {\n                        _qz.file.fileCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.file.fileCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        security: {\n            /** Function used to resolve promise when acquiring site's public certificate. */\n            certHandler: function (resolve, reject) { reject(); },\n            /** Called to create new promise (using {@link _qz.security.certHandler}) for certificate retrieval. */\n            callCert: function () {\n                if (typeof _qz.security.certHandler.then === 'function') {\n                    //already a promise\n                    return _qz.security.certHandler;\n                } else if (_qz.security.certHandler.constructor.name === \"AsyncFunction\") {\n                    //already callable as a promise\n                    return _qz.security.certHandler();\n                } else {\n                    //turn into a promise\n                    return _qz.tools.promise(_qz.security.certHandler);\n                }\n            },\n\n            /** Function used to create promise resolver when requiring signed calls. */\n            signatureFactory: function () { return function (resolve) { resolve(); } },\n            /** Called to create new promise (using {@link _qz.security.signatureFactory}) for signed calls. */\n            callSign: function (toSign) {\n                if (_qz.security.signatureFactory.constructor.name === \"AsyncFunction\") {\n                    //use directly\n                    return _qz.security.signatureFactory(toSign);\n                } else {\n                    //use in a promise\n                    return _qz.tools.promise(_qz.security.signatureFactory(toSign));\n                }\n            },\n\n            /** Signing algorithm used on signatures */\n            signAlgorithm: \"SHA1\",\n\n            needsSigned: function (callName) {\n                const undialoged = [\n                    \"printers.getStatus\",\n                    \"printers.stopListening\",\n                    \"usb.isClaimed\",\n                    \"usb.closeStream\",\n                    \"usb.releaseDevice\",\n                    \"hid.stopListening\",\n                    \"hid.isClaimed\",\n                    \"hid.closeStream\",\n                    \"hid.releaseDevice\",\n                    \"file.stopListening\",\n                    \"getVersion\"\n                ];\n\n                return callName != null && undialoged.indexOf(callName) === -1;\n            }\n        },\n\n\n        tools: {\n            /** Create a new promise */\n            promise: function (resolver) {\n                //prefer global object for historical purposes\n                if (typeof RSVP !== 'undefined') {\n                    return new RSVP.Promise(resolver);\n                } else if (typeof Promise !== 'undefined') {\n                    return new Promise(resolver);\n                } else {\n                    _qz.log.error(\"Promise/A+ support is required.  See qz.api.setPromiseType(...)\");\n                }\n            },\n\n            /** Stub for rejecting with an Error from withing a Promise */\n            reject: function (error) {\n                return _qz.tools.promise(function (resolve, reject) {\n                    reject(error);\n                });\n            },\n\n            stringify: function (object) {\n                //old versions of prototype affect stringify\n                var pjson = Array.prototype.toJSON;\n                delete Array.prototype.toJSON;\n\n                function skipKeys(key, value) {\n                    if (key === \"promise\") {\n                        return undefined;\n                    }\n\n                    return value;\n                }\n\n                var result = JSON.stringify(object, skipKeys);\n\n                if (pjson) {\n                    Array.prototype.toJSON = pjson;\n                }\n\n                return result;\n            },\n\n            hash: function (data) {\n                //prefer global object for historical purposes\n                if (typeof Sha256 !== 'undefined') {\n                    return Sha256.hash(data);\n                } else {\n                    return _qz.SHA.hash(data);\n                }\n            },\n\n            ws: typeof WebSocket !== 'undefined' ? WebSocket : null,\n\n            absolute: function (loc) {\n                if (typeof window !== 'undefined' && typeof document.createElement === 'function') {\n                    var a = document.createElement(\"a\");\n                    a.href = loc;\n                    return a.href;\n                } else if (typeof exports === 'object') {\n                    //node.js\n                    require('path').resolve(loc);\n                }\n                return loc;\n            },\n\n            relative: function (data) {\n                for (var i = 0; i < data.length; i++) {\n                    if (data[i].constructor === Object) {\n                        var absolute = false;\n\n                        if (data[i].data && data[i].data.search && data[i].data.search(/data:image\\/\\w+;base64,/) === 0) {\n                            //upgrade from old base64 behavior\n                            data[i].flavor = \"base64\";\n                            data[i].data = data[i].data.replace(/^data:image\\/\\w+;base64,/, \"\");\n                        } else if (data[i].flavor) {\n                            //if flavor is known, we can directly check for absolute flavor types\n                            if ([\"FILE\", \"XML\"].indexOf(data[i].flavor.toUpperCase()) > -1) {\n                                absolute = true;\n                            }\n                        } else if (data[i].format && [\"HTML\", \"IMAGE\", \"PDF\", \"FILE\", \"XML\"].indexOf(data[i].format.toUpperCase()) > -1) {\n                            //if flavor is not known, all valid pixel formats default to file flavor\n                            //previous v2.0 data also used format as what is now flavor, so we check for those values here too\n                            absolute = true;\n                        } else if (data[i].type && (([\"PIXEL\", \"IMAGE\", \"PDF\"].indexOf(data[i].type.toUpperCase()) > -1 && !data[i].format)\n                            || ([\"HTML\", \"PDF\"].indexOf(data[i].type.toUpperCase()) > -1 && (!data[i].format || data[i].format.toUpperCase() === \"FILE\")))) {\n                            //if all we know is pixel type, then it is image's file flavor\n                            //previous v2.0 data also used type as what is now format, so we check for those value here too\n                            absolute = true;\n                        }\n\n                        if (absolute) {\n                            //change relative links to absolute\n                            data[i].data = _qz.tools.absolute(data[i].data);\n                        }\n                        if (data[i].options && typeof data[i].options.overlay === 'string') {\n                            data[i].options.overlay = _qz.tools.absolute(data[i].options.overlay);\n                        }\n                    }\n                }\n            },\n\n            /** Performs deep copy to target from remaining params */\n            extend: function (target) {\n                //special case when reassigning properties as objects in a deep copy\n                if (typeof target !== 'object') {\n                    target = {};\n                }\n\n                for (var i = 1; i < arguments.length; i++) {\n                    var source = arguments[i];\n                    if (!source) { continue; }\n\n                    for (var key in source) {\n                        if (source.hasOwnProperty(key)) {\n                            if (target === source[key]) { continue; }\n\n                            if (source[key] && source[key].constructor && source[key].constructor === Object) {\n                                var clone;\n                                if (Array.isArray(source[key])) {\n                                    clone = target[key] || [];\n                                } else {\n                                    clone = target[key] || {};\n                                }\n\n                                target[key] = _qz.tools.extend(clone, source[key]);\n                            } else if (source[key] !== undefined) {\n                                target[key] = source[key];\n                            }\n                        }\n                    }\n                }\n\n                return target;\n            },\n\n            versionCompare: function (major, minor, patch, build) {\n                if (_qz.tools.assertActive()) {\n                    var semver = _qz.websocket.connection.semver;\n                    if (semver[0] != major) {\n                        return semver[0] - major;\n                    }\n                    if (minor != undefined && semver[1] != minor) {\n                        return semver[1] - minor;\n                    }\n                    if (patch != undefined && semver[2] != patch) {\n                        return semver[2] - patch;\n                    }\n                    if (build != undefined && semver.length > 3 && semver[3] != build) {\n                        return Number.isInteger(semver[3]) && Number.isInteger(build) ? semver[3] - build : semver[3].toString().localeCompare(build.toString());\n                    }\n                    return 0;\n                }\n            },\n\n            isVersion: function (major, minor, patch, build) {\n                return _qz.tools.versionCompare(major, minor, patch, build) == 0;\n            },\n\n            isActive: function () {\n                return _qz.websocket.connection != null && _qz.websocket.connection.established;\n            },\n\n            assertActive: function () {\n                if (_qz.tools.isActive()) {\n                    return true;\n                }\n                // Promise won't reject on throw; yet better than 'undefined'\n                throw new Error(\"A connection to QZ has not been established yet\");\n            },\n\n            uint8ArrayToHex: function (uint8) {\n                return Array.from(uint8)\n                    .map(function (i) { return i.toString(16).padStart(2, '0'); })\n                    .join('');\n            },\n\n            uint8ArrayToBase64: function (uint8) {\n                /**\n                 * Adapted from Egor Nepomnyaschih's code under MIT Licence (C) 2020\n                 * see https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727\n                 */\n                var map = [\n                    \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\",\n                    \"V\", \"W\", \"X\", \"Y\", \"Z\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\",\n                    \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"+\", \"/\"\n                ];\n\n                var result = '', i, l = uint8.length;\n                for (i = 2; i < l; i += 3) {\n                    result += map[uint8[i - 2] >> 2];\n                    result += map[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];\n                    result += map[((uint8[i - 1] & 0x0F) << 2) | (uint8[i] >> 6)];\n                    result += map[uint8[i] & 0x3F];\n                }\n                if (i === l + 1) { // 1 octet yet to write\n                    result += map[uint8[i - 2] >> 2];\n                    result += map[(uint8[i - 2] & 0x03) << 4];\n                    result += \"==\";\n                }\n                if (i === l) { // 2 octets yet to write\n                    result += map[uint8[i - 2] >> 2];\n                    result += map[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];\n                    result += map[(uint8[i - 1] & 0x0F) << 2];\n                    result += \"=\";\n                }\n                return result;\n            },\n        },\n\n        compatible: {\n            /** Converts message format to a previous version's */\n            data: function (printData) {\n                // special handling for Uint8Array\n                if (printData.constructor === Object && printData.data instanceof Uint8Array) {\n                    if (printData.flavor) {\n                        var flavor = printData.flavor.toString().toUpperCase();\n                        switch (flavor) {\n                            case 'BASE64':\n                                printData.data = _qz.tools.uint8ArrayToBase64(printData.data);\n                                break;\n                            case 'HEX':\n                                printData.data = _qz.tools.uint8ArrayToHex(printData.data);\n                                break;\n                            default:\n                                throw new Error(\"Uint8Array conversion to '\" + flavor + \"' is not supported.\");\n                        }\n                    }\n                }\n\n                if (_qz.tools.isVersion(2, 0)) {\n                    /*\n                    2.0.x conversion\n                    -----\n                    type=pixel -> use format as 2.0 type (unless 'command' format, which forces 2.0 'raw' type)\n                    type=raw -> 2.0 type has to be 'raw'\n                                if format is 'image' -> force 2.0 'image' format, ignore everything else (unsupported in 2.0)\n\n                     flavor translates straight to 2.0 format (unless forced to 'raw'/'image')\n                     */\n                    _qz.log.trace(\"Converting print data to v2.0 for \" + _qz.websocket.connection.version);\n                    for (var i = 0; i < printData.length; i++) {\n                        if (printData[i].constructor === Object) {\n                            if (printData[i].type && printData[i].type.toUpperCase() === \"RAW\" && printData[i].format && printData[i].format.toUpperCase() === \"IMAGE\") {\n                                if (printData[i].flavor && printData[i].flavor.toUpperCase() === \"BASE64\") {\n                                    //special case for raw base64 images\n                                    printData[i].data = \"data:image/compat;base64,\" + printData[i].data;\n                                }\n                                printData[i].flavor = \"IMAGE\"; //forces 'image' format when shifting for conversion\n                            }\n                            if ((printData[i].type && printData[i].type.toUpperCase() === \"RAW\") || (printData[i].format && printData[i].format.toUpperCase() === \"COMMAND\")) {\n                                printData[i].format = \"RAW\"; //forces 'raw' type when shifting for conversion\n                            }\n\n                            printData[i].type = printData[i].format;\n                            printData[i].format = printData[i].flavor;\n                            delete printData[i].flavor;\n                        }\n                    }\n                }\n            },\n\n            /* Converts config defaults to match previous version */\n            config: function (config, dirty) {\n                if (_qz.tools.isVersion(2, 0)) {\n                    if (!dirty.rasterize) {\n                        config.rasterize = true;\n                    }\n                }\n                if (_qz.tools.versionCompare(2, 2) < 0) {\n                    if (config.forceRaw !== 'undefined') {\n                        config.altPrinting = config.forceRaw;\n                        delete config.forceRaw;\n                    }\n                }\n                if (_qz.tools.versionCompare(2, 1, 2, 11) < 0) {\n                    if (config.spool) {\n                        if (config.spool.size) {\n                            config.perSpool = config.spool.size;\n                            delete config.spool.size;\n                        }\n                        if (config.spool.end) {\n                            config.endOfDoc = config.spool.end;\n                            delete config.spool.end;\n                        }\n                        delete config.spool;\n                    }\n                }\n                return config;\n            },\n\n            /** Compat wrapper with previous version **/\n            networking: function (hostname, port, signature, signingTimestamp, mappingCallback) {\n                // Use 2.0\n                if (_qz.tools.isVersion(2, 0)) {\n                    return _qz.tools.promise(function (resolve, reject) {\n                        _qz.websocket.dataPromise('websocket.getNetworkInfo', {\n                            hostname: hostname,\n                            port: port\n                        }, signature, signingTimestamp).then(function (data) {\n                            if (typeof mappingCallback !== 'undefined') {\n                                resolve(mappingCallback(data));\n                            } else {\n                                resolve(data);\n                            }\n                        }, reject);\n                    });\n                }\n                // Wrap 2.1\n                return _qz.tools.promise(function (resolve, reject) {\n                    _qz.websocket.dataPromise('networking.device', {\n                        hostname: hostname,\n                        port: port\n                    }, signature, signingTimestamp).then(function (data) {\n                        resolve({ ipAddress: data.ip, macAddress: data.mac });\n                    }, reject);\n                });\n            },\n\n            /** Check if QZ version supports chosen algorithm */\n            algorithm: function (quiet) {\n                //if not connected yet we will assume compatibility exists for the time being\n                if (_qz.tools.isActive()) {\n                    if (_qz.tools.isVersion(2, 0)) {\n                        if (!quiet) {\n                            _qz.log.warn(\"Connected to an older version of QZ, alternate signature algorithms are not supported\");\n                        }\n                        return false;\n                    }\n                }\n\n                return true;\n            }\n        },\n\n        /**\n         * Adapted from Chris Veness's code under MIT Licence (C) 2002\n         * see http://www.movable-type.co.uk/scripts/sha256.html\n         */\n        SHA: {\n            //@formatter:off - keep this block compact\n            hash: function (msg) {\n                // add trailing '1' bit (+ 0's padding) to string [§5.1.1]\n                msg = _qz.SHA._utf8Encode(msg) + String.fromCharCode(0x80);\n\n                // constants [§4.2.2]\n                var K = [\n                    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n                    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n                    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n                    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n                    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n                    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n                    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n                    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n                ];\n                // initial hash value [§5.3.1]\n                var H = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];\n\n                // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]\n                var l = msg.length / 4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length\n                var N = Math.ceil(l / 16);  // number of 16-integer-blocks required to hold 'l' ints\n                var M = new Array(N);\n\n                for (var i = 0; i < N; i++) {\n                    M[i] = new Array(16);\n                    for (var j = 0; j < 16; j++) {  // encode 4 chars per integer, big-endian encoding\n                        M[i][j] = (msg.charCodeAt(i * 64 + j * 4) << 24) | (msg.charCodeAt(i * 64 + j * 4 + 1) << 16) |\n                            (msg.charCodeAt(i * 64 + j * 4 + 2) << 8) | (msg.charCodeAt(i * 64 + j * 4 + 3));\n                    } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0\n                }\n                // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]\n                // note: most significant word would be (len-1)*8 >>> 32, but since JS converts\n                // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators\n                M[N - 1][14] = ((msg.length - 1) * 8) / Math.pow(2, 32);\n                M[N - 1][14] = Math.floor(M[N - 1][14]);\n                M[N - 1][15] = ((msg.length - 1) * 8) & 0xffffffff;\n\n                // HASH COMPUTATION [§6.1.2]\n                var W = new Array(64); var a, b, c, d, e, f, g, h;\n                for (var i = 0; i < N; i++) {\n                    // 1 - prepare message schedule 'W'\n                    for (var t = 0; t < 16; t++) { W[t] = M[i][t]; }\n                    for (var t = 16; t < 64; t++) { W[t] = (_qz.SHA._dev1(W[t - 2]) + W[t - 7] + _qz.SHA._dev0(W[t - 15]) + W[t - 16]) & 0xffffffff; }\n                    // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value\n                    a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7];\n                    // 3 - main loop (note 'addition modulo 2^32')\n                    for (var t = 0; t < 64; t++) {\n                        var T1 = h + _qz.SHA._sig1(e) + _qz.SHA._ch(e, f, g) + K[t] + W[t];\n                        var T2 = _qz.SHA._sig0(a) + _qz.SHA._maj(a, b, c);\n                        h = g; g = f; f = e; e = (d + T1) & 0xffffffff;\n                        d = c; c = b; b = a; a = (T1 + T2) & 0xffffffff;\n                    }\n                    // 4 - compute the new intermediate hash value (note 'addition modulo 2^32')\n                    H[0] = (H[0] + a) & 0xffffffff; H[1] = (H[1] + b) & 0xffffffff; H[2] = (H[2] + c) & 0xffffffff; H[3] = (H[3] + d) & 0xffffffff;\n                    H[4] = (H[4] + e) & 0xffffffff; H[5] = (H[5] + f) & 0xffffffff; H[6] = (H[6] + g) & 0xffffffff; H[7] = (H[7] + h) & 0xffffffff;\n                }\n\n                return _qz.SHA._hexStr(H[0]) + _qz.SHA._hexStr(H[1]) + _qz.SHA._hexStr(H[2]) + _qz.SHA._hexStr(H[3]) +\n                    _qz.SHA._hexStr(H[4]) + _qz.SHA._hexStr(H[5]) + _qz.SHA._hexStr(H[6]) + _qz.SHA._hexStr(H[7]);\n            },\n\n            // Rotates right (circular right shift) value x by n positions\n            _rotr: function (n, x) { return (x >>> n) | (x << (32 - n)); },\n            // logical functions\n            _sig0: function (x) { return _qz.SHA._rotr(2, x) ^ _qz.SHA._rotr(13, x) ^ _qz.SHA._rotr(22, x); },\n            _sig1: function (x) { return _qz.SHA._rotr(6, x) ^ _qz.SHA._rotr(11, x) ^ _qz.SHA._rotr(25, x); },\n            _dev0: function (x) { return _qz.SHA._rotr(7, x) ^ _qz.SHA._rotr(18, x) ^ (x >>> 3); },\n            _dev1: function (x) { return _qz.SHA._rotr(17, x) ^ _qz.SHA._rotr(19, x) ^ (x >>> 10); },\n            _ch: function (x, y, z) { return (x & y) ^ (~x & z); },\n            _maj: function (x, y, z) { return (x & y) ^ (x & z) ^ (y & z); },\n            // note can't use toString(16) as it is implementation-dependant, and in IE returns signed numbers when used on full words\n            _hexStr: function (n) { var s = \"\", v; for (var i = 7; i >= 0; i--) { v = (n >>> (i * 4)) & 0xf; s += v.toString(16); } return s; },\n            // implementation of deprecated unescape() based on https://cwestblog.com/2011/05/23/escape-unescape-deprecated/ (and comments)\n            _unescape: function (str) {\n                return str.replace(/%(u[\\da-f]{4}|[\\da-f]{2})/gi, function (seq) {\n                    if (seq.length - 1) {\n                        return String.fromCharCode(parseInt(seq.substring(seq.length - 3 ? 2 : 1), 16))\n                    } else {\n                        var code = seq.charCodeAt(0);\n                        return code < 256 ? \"%\" + (0 + code.toString(16)).slice(-2).toUpperCase() : \"%u\" + (\"000\" + code.toString(16)).slice(-4).toUpperCase()\n                    }\n                });\n            },\n            _utf8Encode: function (str) {\n                return _qz.SHA._unescape(encodeURIComponent(str));\n            }\n            //@formatter:on\n        },\n    };\n\n\n    ///// CONFIG CLASS ////\n\n    /** Object to handle configured printer options. */\n    function Config(printer, opts) {\n\n        this.config = _qz.tools.extend({}, _qz.printing.defaultConfig); //create a copy of the default options\n        this._dirtyOpts = {}; //track which config options have changed from the defaults\n\n        /**\n         * Set the printer assigned to this config.\n         * @param {string|Object} newPrinter Name of printer. Use object type to specify printing to file or host.\n         *  @param {string} [newPrinter.name] Name of printer to send printing.\n         *  @param {string} [newPrinter.file] DEPRECATED: Name of file to send printing.\n         *  @param {string} [newPrinter.host] IP address or host name to send printing.\n         *  @param {string} [newPrinter.port] Port used by &lt;printer.host>.\n         */\n        this.setPrinter = function (newPrinter) {\n            if (typeof newPrinter === 'string') {\n                newPrinter = { name: newPrinter };\n            }\n\n            if (newPrinter && newPrinter.file) {\n                // TODO: Warn for UNC paths too https://github.com/qzind/tray/issues/730\n                if (newPrinter.file.indexOf(\"\\\\\\\\\") != 0) {\n                    _qz.log.warn(\"Printing to file is deprecated.  See https://github.com/qzind/tray/issues/730\");\n                }\n            }\n\n            this.printer = newPrinter;\n        };\n\n        /**\n         *  @returns {Object} The printer currently assigned to this config.\n         */\n        this.getPrinter = function () {\n            return this.printer;\n        };\n\n        /**\n         * Alter any of the printer options currently applied to this config.\n         * @param newOpts {Object} The options to change. See <code>qz.configs.setDefaults</code> docs for available values.\n         *\n         * @see qz.configs.setDefaults\n         */\n        this.reconfigure = function (newOpts) {\n            for (var key in newOpts) {\n                if (newOpts[key] !== undefined) {\n                    this._dirtyOpts[key] = true;\n                }\n            }\n\n            _qz.tools.extend(this.config, newOpts);\n        };\n\n        /**\n         * @returns {Object} The currently applied options on this config.\n         */\n        this.getOptions = function () {\n            return _qz.compatible.config(this.config, this._dirtyOpts);\n        };\n\n        // init calls for new config object\n        this.setPrinter(printer);\n        this.reconfigure(opts);\n    }\n\n    /**\n     * Shortcut method for calling <code>qz.print</code> with a particular config.\n     * @param {Array<Object|string>} data Array of data being sent to the printer. See <code>qz.print</code> docs for available values.\n     * @param {boolean} [signature] Pre-signed signature of JSON string containing <code>call</code>, <code>params</code>, and <code>timestamp</code>.\n     * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n     *\n     * @example\n     * qz.print(myConfig, ...); // OR\n     * myConfig.print(...);\n     *\n     * @see qz.print\n     */\n    Config.prototype.print = function (data, signature, signingTimestamp) {\n        qz.print(this, data, signature, signingTimestamp);\n    };\n\n\n    ///// PUBLIC METHODS /////\n\n    /** @namespace qz */\n    var qz = {\n\n        /**\n         * Calls related specifically to the web socket connection.\n         * @namespace qz.websocket\n         */\n        websocket: {\n            /**\n             * Check connection status. Active connection is necessary for other calls to run.\n             *\n             * @returns {boolean} If there is an active connection with QZ Tray.\n             *\n             * @see connect\n             *\n             * @memberof  qz.websocket\n             */\n            isActive: function () {\n                return _qz.tools.isActive();\n            },\n\n            /**\n             * Call to setup connection with QZ Tray on user's system.\n             *\n             * @param {Object} [options] Configuration options for the web socket connection.\n             *  @param {string|Array<string>} [options.host=['localhost', 'localhost.qz.io']] Host running the QZ Tray software.\n             *  @param {Object} [options.port] Config options for ports to cycle.\n             *   @param {Array<number>} [options.port.secure=[8181, 8282, 8383, 8484]] Array of secure (WSS) ports to try\n             *   @param {Array<number>} [options.port.insecure=[8182, 8283, 8384, 8485]] Array of insecure (WS) ports to try\n             *  @param {boolean} [options.usingSecure=true] If the web socket should try to use secure ports for connecting.\n             *  @param {number} [options.keepAlive=60] Seconds between keep-alive pings to keep connection open. Set to 0 to disable.\n             *  @param {number} [options.retries=0] Number of times to reconnect before failing.\n             *  @param {number} [options.delay=0] Seconds before firing a connection.  Ignored if <code>options.retries</code> is 0.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.websocket\n             */\n            connect: function (options) {\n                return _qz.tools.promise(function (resolve, reject) {\n                    if (_qz.tools.isActive()) {\n                        reject(new Error(\"An open connection with QZ Tray already exists\"));\n                        return;\n                    } else if (_qz.websocket.connection != null) {\n                        reject(new Error(\"The current connection attempt has not returned yet\"));\n                        return;\n                    }\n\n                    if (!_qz.tools.ws) {\n                        reject(new Error(\"WebSocket not supported by this browser\"));\n                        return;\n                    } else if (!_qz.tools.ws.CLOSED || _qz.tools.ws.CLOSED == 2) {\n                        reject(new Error(\"Unsupported WebSocket version detected: HyBi-00/Hixie-76\"));\n                        return;\n                    }\n\n                    //ensure some form of options exists for value checks\n                    if (options == undefined) { options = {}; }\n\n                    //disable secure ports if page is not secure\n                    if (typeof location === 'undefined' || location.protocol !== 'https:') {\n                        //respect forcing secure ports if it is defined, otherwise disable\n                        if (typeof options.usingSecure === 'undefined') {\n                            _qz.log.trace(\"Disabling secure ports due to insecure page\");\n                            options.usingSecure = false;\n                        }\n                    }\n\n                    //ensure any hosts are passed to internals as an array\n                    if (typeof options.host !== 'undefined' && !Array.isArray(options.host)) {\n                        options.host = [options.host];\n                    }\n\n                    var attempt = function (count) {\n                        var tried = false;\n                        var nextAttempt = function () {\n                            if (!tried) {\n                                tried = true;\n\n                                if (options && count < options.retries) {\n                                    attempt(count + 1);\n                                } else {\n                                    _qz.websocket.connection = null;\n                                    reject.apply(null, arguments);\n                                }\n                            }\n                        };\n\n                        var delayed = function () {\n                            var config = _qz.tools.extend({}, _qz.websocket.connectConfig, options);\n                            _qz.websocket.setup.findConnection(config, resolve, nextAttempt)\n                        };\n                        if (count == 0) {\n                            delayed(); // only retries will be called with a delay\n                        } else {\n                            setTimeout(delayed, options.delay * 1000);\n                        }\n                    };\n\n                    attempt(0);\n                });\n            },\n\n            /**\n             * Stop any active connection with QZ Tray.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.websocket\n             */\n            disconnect: function () {\n                return _qz.tools.promise(function (resolve, reject) {\n                    if (_qz.tools.isActive()) {\n                        _qz.websocket.connection.close();\n                        _qz.websocket.connection.promise = { resolve: resolve, reject: reject };\n                    } else {\n                        reject(new Error(\"No open connection with QZ Tray\"))\n                    }\n                });\n            },\n\n            /**\n             * List of functions called for any connections errors outside of an API call.<p/>\n             * Also called if {@link websocket#connect} fails to connect.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Event} event)</code> calls.\n             *\n             * @memberof qz.websocket\n             */\n            setErrorCallbacks: function (calls) {\n                _qz.websocket.errorCallbacks = calls;\n            },\n\n            /**\n             * List of functions called for any connection closing event outside of an API call.<p/>\n             * Also called when {@link websocket#disconnect} is called.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Event} event)</code> calls.\n             *\n             * @memberof qz.websocket\n             */\n            setClosedCallbacks: function (calls) {\n                _qz.websocket.closedCallbacks = calls;\n            },\n\n            /**\n             * @deprecated Since 2.1.0.  Please use qz.networking.device() instead\n             *\n             * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] Port to use with custom hostname, defaults to 443\n             * @param {string} [signature] Pre-signed signature of hashed JSON string containing <code>call='websocket.getNetworkInfo'</code>, <code>params</code> object, and <code>timestamp</code>.\n             * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n             *\n             * @returns {Promise<Object<{ipAddress: string, macAddress: string}>|Error>} Connected system's network information.\n             *\n             * @memberof qz.websocket\n             */\n            getNetworkInfo: _qz.compatible.networking,\n\n            /**\n             * @returns {Object<{socket: String, host: String, port: Number}>} Details of active websocket connection\n             *\n             * @memberof qz.websocket\n             */\n            getConnectionInfo: function () {\n                if (_qz.tools.assertActive()) {\n                    var url = _qz.websocket.connection.url.split(/[:\\/]+/g);\n                    return { socket: url[0], host: url[1], port: +url[2] };\n                }\n            }\n        },\n\n\n        /**\n         * Calls related to getting printer information from the connection.\n         * @namespace qz.printers\n         */\n        printers: {\n            /**\n             * @param {string} [signature] Pre-signed signature of hashed JSON string containing <code>call='printers.getDefault</code>, <code>params</code>, and <code>timestamp</code>.\n             * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n             *\n             * @returns {Promise<string|Error>} Name of the connected system's default printer.\n             *\n             * @memberof qz.printers\n             */\n            getDefault: function (signature, signingTimestamp) {\n                return _qz.websocket.dataPromise('printers.getDefault', null, signature, signingTimestamp);\n            },\n\n            /**\n             * @param {string} [query] Search for a specific printer. All printers are returned if not provided.\n             * @param {string} [signature] Pre-signed signature of hashed JSON string containing <code>call='printers.find'</code>, <code>params</code>, and <code>timestamp</code>.\n             * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n             *\n             * @returns {Promise<Array<string>|string|Error>} The matched printer name if <code>query</code> is provided.\n             *                                                Otherwise an array of printer names found on the connected system.\n             *\n             * @memberof qz.printers\n             */\n            find: function (query, signature, signingTimestamp) {\n                return _qz.websocket.dataPromise('printers.find', { query: query }, signature, signingTimestamp);\n            },\n\n            /**\n             * Provides a list, with additional information, for each printer available to QZ.\n             *\n             * @returns {Promise<Array<Object>|Object|Error>}\n             *\n             * @memberof qz.printers\n             */\n            details: function () {\n                return _qz.websocket.dataPromise('printers.detail');\n            },\n\n            /**\n             * Start listening for printer status events, such as paper_jam events.\n             * Reported under the ACTION type in the streamEvent on callbacks.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.printers.setPrinterCallbacks\n             *\n             * @param {null|string|Array<string>} printers Printer or list of printers to listen to, null listens to all.\n             * @param {Object|null} [options] Printer listener options\n             *  @param {null|boolean} [options.jobData=false] Flag indicating if raw spool file content should be return as well as status information (Windows only)\n             *  @param {null|number} [options.maxJobData=-1] Maximum number of bytes to returns for raw spooled file content (Windows only)\n             *  @param {null|string} [options.flavor=\"plain\"] Flavor of data format returned. Valid flavors are <code>[base64 | hex | plain*]</code> (Windows only)\n             *\n             * @memberof qz.printers\n             */\n            startListening: function (printers, options) {\n                if (!Array.isArray(printers)) {\n                    printers = [printers];\n                }\n                var params = {\n                    printerNames: printers\n                };\n                if (options && options.jobData == true) params.jobData = true;\n                if (options && options.maxJobData) params.maxJobData = options.maxJobData;\n                if (options && options.flavor) params.flavor = options.flavor;\n                return _qz.websocket.dataPromise('printers.startListening', params);\n            },\n\n            /**\n             * Stop listening for printer status actions.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.printers.setPrinterCallbacks\n             *\n             * @memberof qz.printers\n             */\n            stopListening: function () {\n                return _qz.websocket.dataPromise('printers.stopListening');\n            },\n\n            /**\n             * Retrieve current printer status from any active listeners.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.printers.startListening\n             *\n             * @memberof qz.printers\n             */\n            getStatus: function () {\n                return _qz.websocket.dataPromise('printers.getStatus');\n            },\n\n            /**\n             * List of functions called for any printer status change.\n             * Event data will contain <code>{string} printerName</code> and <code>{string} status</code> for all types.\n             *  For RECEIVE types, <code>{Array} output</code> (in hexadecimal format).\n             *  For ERROR types, <code>{string} exception</code>.\n             *  For ACTION types, <code>{string} actionType</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             * @since 2.1.0\n             *\n             * @memberof qz.printers\n             */\n            setPrinterCallbacks: function (calls) {\n                _qz.printers.printerCallbacks = calls;\n            }\n        },\n\n        /**\n         * Calls related to setting up new printer configurations.\n         * @namespace qz.configs\n         */\n        configs: {\n            /**\n             * Default options used by new configs if not overridden.\n             * Setting a value to NULL will use the printer's default options.\n             * Updating these will not update the options on any created config.\n             *\n             * @param {Object} options Default options used by printer configs if not overridden.\n             *\n             *  @param {Object} [options.bounds=null] Bounding box rectangle.\n             *   @param {number} [options.bounds.x=0] Distance from left for bounding box starting corner\n             *   @param {number} [options.bounds.y=0] Distance from top for bounding box starting corner\n             *   @param {number} [options.bounds.width=0] Width of bounding box\n             *   @param {number} [options.bounds.height=0] Height of bounding box\n             *  @param {string} [options.colorType='color'] Valid values <code>[color | grayscale | blackwhite]</code>\n             *  @param {number} [options.copies=1] Number of copies to be printed.\n             *  @param {number|Array<number>|Object|Array<Object>|string} [options.density=0] Pixel density (DPI, DPMM, or DPCM depending on <code>[options.units]</code>).\n             *      If provided as an array, uses the first supported density found (or the first entry if none found).\n             *      If provided as a string, valid values are <code>[best | draft]</code>, corresponding to highest or lowest reported density respectively.\n             *  @param {number} [options.density.cross=0] Asymmetric pixel density for the cross feed direction.\n             *  @param {number} [options.density.feed=0] Asymmetric pixel density for the feed direction.\n             *  @param {boolean|string} [options.duplex=false] Double sided printing, Can specify duplex style by passing a string value: <code>[one-sided | duplex | long-edge | tumble | short-edge]</code>\n             *  @param {number} [options.fallbackDensity=null] Value used when default density value cannot be read, or in cases where reported as \"Normal\" by the driver, (in DPI, DPMM, or DPCM depending on <code>[options.units]</code>).\n             *  @param {string} [options.interpolation='bicubic'] Valid values <code>[bicubic | bilinear | nearest-neighbor]</code>. Controls how images are handled when resized.\n             *  @param {string} [options.jobName=null] Name to display in print queue.\n             *  @param {boolean} [options.legacy=false] If legacy style printing should be used.\n             *  @param {Object|number} [options.margins=0] If just a number is provided, it is used as the margin for all sides.\n             *   @param {number} [options.margins.top=0]\n             *   @param {number} [options.margins.right=0]\n             *   @param {number} [options.margins.bottom=0]\n             *   @param {number} [options.margins.left=0]\n             *  @param {string} [options.orientation=null] Valid values <code>[portrait | landscape | reverse-landscape | null]</code>.\n             *                                             If set to <code>null</code>, orientation will be determined automatically.\n             *  @param {number} [options.paperThickness=null]\n             *  @param {string|number} [options.printerTray=null] Printer tray to pull from. The number N assumes string equivalent of 'Tray N'. Uses printer default if NULL.\n             *  @param {boolean} [options.rasterize=false] Whether documents should be rasterized before printing.\n             *                                             Specifying <code>[options.density]</code> for PDF print formats will set this to <code>true</code>.\n             *  @param {number} [options.rotation=0] Image rotation in degrees.\n             *  @param {boolean} [options.scaleContent=true] Scales print content to page size, keeping ratio.\n             *  @param {Object} [options.size=null] Paper size.\n             *   @param {number} [options.size.width=null] Page width.\n             *   @param {number} [options.size.height=null] Page height.\n             *  @param {string} [options.units='in'] Page units, applies to paper size, margins, and density. Valid value <code>[in | cm | mm]</code>\n             *\n             *  @param {boolean} [options.forceRaw=false] Print the specified raw data using direct method, skipping the driver.  Not yet supported on Windows.\n             *  @param {string|Object} [options.encoding=null] Character set for commands. Can be provided as an object for converting encoding types for RAW types.\n             *   @param {string} [options.encoding.from] If this encoding type is provided, RAW type commands will be parsed from this for the purpose of being converted to the <code>encoding.to</code> value.\n             *   @param {string} [options.encoding.to] Encoding RAW type commands will be converted into. If <Code>encoding.from</code> is not provided, this will be treated as if a string was passed for encoding.\n             *  @param {string} [options.endOfDoc=null] DEPRECATED Raw only: Character(s) denoting end of a page to control spooling.\n             *  @param {number} [options.perSpool=1] DEPRECATED: Raw only: Number of pages per spool.\n             *  @param {boolean} [options.retainTemp=false] Retain any temporary files used.  Ignored unless <code>forceRaw</code> <code>true</code>.\n             *  @param {Object} [options.spool=null] Advanced spooling options.\n             *   @param {number} [options.spool.size=null] Number of pages per spool.  Default is no limit.  If <code>spool.end</code> is provided, defaults to <code>1</code>\n             *   @param {string} [options.spool.end=null] Raw only: Character(s) denoting end of a page to control spooling.\n             *\n             * @memberof qz.configs\n             */\n            setDefaults: function (options) {\n                _qz.tools.extend(_qz.printing.defaultConfig, options);\n            },\n\n            /**\n             * Creates new printer config to be used in printing.\n             *\n             * @param {string|object} printer Name of printer. Use object type to specify printing to file or host.\n             *  @param {string} [printer.name] Name of printer to send printing.\n             *  @param {string} [printer.file] Name of file to send printing.\n             *  @param {string} [printer.host] IP address or host name to send printing.\n             *  @param {string} [printer.port] Port used by &lt;printer.host>.\n             * @param {Object} [options] Override any of the default options for this config only.\n             *\n             * @returns {Config} The new config.\n             *\n             * @see configs.setDefaults\n             *\n             * @memberof qz.configs\n             */\n            create: function (printer, options) {\n                return new Config(printer, options);\n            }\n        },\n\n\n        /**\n         * Send data to selected config for printing.\n         * The promise for this method will resolve when the document has been sent to the printer. Actual printing may not be complete.\n         * <p/>\n         * Optionally, print requests can be pre-signed:\n         * Signed content consists of a JSON object string containing no spacing,\n         * following the format of the \"call\" and \"params\" keys in the API call, with the addition of a \"timestamp\" key in milliseconds\n         * ex. <code>'{\"call\":\"<callName>\",\"params\":{...},\"timestamp\":1450000000}'</code>\n         *\n         * @param {Object<Config>|Array<Object<Config>>} configs Previously created config object or objects.\n         * @param {Array<Object|string>|Array<Array<Object|string>>} data Array of data being sent to the printer.<br/>\n         *      String values are interpreted as <code>{type: 'raw', format: 'command', flavor: 'plain', data: &lt;string>}</code>.\n         *  @param {string} data.data\n         *  @param {string} data.type Printing type. Valid types are <code>[pixel | raw*]</code>. *Default\n         *  @param {string} data.format Format of data type used. *Default per type<p/>\n         *      For <code>[pixel]</code> types, valid formats are <code>[html | image* | pdf]</code>.<p/>\n         *      For <code>[raw]</code> types, valid formats are <code>[command* | html | image | pdf]</code>.\n         *  @param {string} data.flavor Flavor of data format used. *Default per format<p/>\n         *      For <code>[command]</code> formats, valid flavors are <code>[base64 | file | hex | plain* | xml]</code>.<p/>\n         *      For <code>[html]</code> formats, valid flavors are <code>[file* | plain]</code>.<p/>\n         *      For <code>[image]</code> formats, valid flavors are <code>[base64 | file*]</code>.<p/>\n         *      For <code>[pdf]</code> formats, valid flavors are <code>[base64 | file*]</code>.\n         *  @param {Object} [data.options]\n         *   @param {string} [data.options.language] Required with <code>[raw]</code> type + <code>[image]</code> format. Printer language.\n         *   @param {number} [data.options.x] Optional with <code>[raw]</code> type + <code>[image]</code> format. The X position of the image.\n         *   @param {number} [data.options.y] Optional with <code>[raw]</code> type + <code>[image]</code> format. The Y position of the image.\n         *   @param {string|number} [data.options.dotDensity] Optional with <code>[raw]</code> type + <code>[image]</code> format.\n         *   @param {number} [data.precision=128] Optional with <code>[raw]</code> type <code>[image]</code> format. Bit precision of the ribbons.\n         *   @param {boolean|string|Array<Array<number>>} [data.options.overlay=false] Optional with <code>[raw]</code> type <code>[image]</code> format.\n         *       Boolean sets entire layer, string sets mask image, Array sets array of rectangles in format <code>[x1,y1,x2,y2]</code>.\n         *   @param {string} [data.options.xmlTag] Required with <code>[xml]</code> flavor. Tag name containing base64 formatted data.\n         *   @param {number} [data.options.pageWidth] Optional with <code>[html | pdf]</code> formats. Width of the rendering.\n         *       Defaults to paper width.\n         *   @param {number} [data.options.pageHeight] Optional with <code>[html | pdf]</code> formats. Height of the rendering.\n         *       Defaults to paper height for <code>[pdf]</code>, or auto sized for <code>[html]</code>.\n         *   @param {string} [data.options.pageRanges] Optional with <code>[pdf]</code> formats. Comma-separated list of page ranges to include.\n         *   @param {boolean} [data.options.ignoreTransparency=false] Optional with <code>[pdf]</code> formats. Instructs transparent PDF elements to be ignored.\n         *       Transparent PDF elements are known to degrade performance and quality when printing.\n         * @param {...*} [arguments] Additionally three more parameters can be specified:<p/>\n         *     <code>{boolean} [resumeOnError=false]</code> Whether the chain should continue printing if it hits an error on one the the prints.<p/>\n         *     <code>{string|Array<string>} [signature]</code> Pre-signed signature(s) of the JSON string for containing <code>call</code>, <code>params</code>, and <code>timestamp</code>.<p/>\n         *     <code>{number|Array<number>} [signingTimestamps]</code> Required to match with <code>signature</code>. Timestamps for each of the passed pre-signed content.\n         *\n         * @returns {Promise<null|Error>}\n         *\n         * @see qz.configs.create\n         *\n         * @memberof qz\n         */\n        print: function (configs, data) {\n            var resumeOnError = false,\n                signatures = [],\n                signaturesTimestamps = [];\n\n            //find optional parameters\n            if (arguments.length >= 3) {\n                if (typeof arguments[2] === 'boolean') {\n                    resumeOnError = arguments[2];\n\n                    if (arguments.length >= 5) {\n                        signatures = arguments[3];\n                        signaturesTimestamps = arguments[4];\n                    }\n                } else if (arguments.length >= 4) {\n                    signatures = arguments[2];\n                    signaturesTimestamps = arguments[3];\n                }\n\n                //ensure values are arrays for consistency\n                if (signatures && !Array.isArray(signatures)) { signatures = [signatures]; }\n                if (signaturesTimestamps && !Array.isArray(signaturesTimestamps)) { signaturesTimestamps = [signaturesTimestamps]; }\n            }\n\n            if (!Array.isArray(configs)) { configs = [configs]; } //single config -> array of configs\n            if (!Array.isArray(data[0])) { data = [data]; } //single data array -> array of data arrays\n\n            //clean up data formatting\n            for (var d = 0; d < data.length; d++) {\n                _qz.tools.relative(data[d]);\n                _qz.compatible.data(data[d]);\n            }\n\n            var sendToPrint = function (mapping) {\n                var params = {\n                    printer: mapping.config.getPrinter(),\n                    options: mapping.config.getOptions(),\n                    data: mapping.data\n                };\n\n                return _qz.websocket.dataPromise('print', params, mapping.signature, mapping.timestamp);\n            };\n\n            //chain instead of Promise.all, so resumeOnError can collect each error\n            var chain = [];\n            for (var i = 0; i < configs.length || i < data.length; i++) {\n                (function (i_) {\n                    var map = {\n                        config: configs[Math.min(i_, configs.length - 1)],\n                        data: data[Math.min(i_, data.length - 1)],\n                        signature: signatures[i_],\n                        timestamp: signaturesTimestamps[i_]\n                    };\n\n                    chain.push(function () { return sendToPrint(map) });\n                })(i);\n            }\n\n            //setup to catch errors if needed\n            var fallThrough = null;\n            if (resumeOnError) {\n                var fallen = [];\n                fallThrough = function (err) { fallen.push(err); };\n\n                //final promise to reject any errors as a group\n                chain.push(function () {\n                    return _qz.tools.promise(function (resolve, reject) {\n                        fallen.length ? reject(fallen) : resolve();\n                    });\n                });\n            }\n\n            var last = null;\n            chain.reduce(function (sequence, link) {\n                last = sequence.catch(fallThrough).then(link); //catch is ignored if fallThrough is null\n                return last;\n            }, _qz.tools.promise(function (r) { r(); })); //an immediately resolved promise to start off the chain\n\n            //return last promise so users can chain off final action or catch when stopping on error\n            return last;\n        },\n\n\n        /**\n         * Calls related to interaction with serial ports.\n         * @namespace qz.serial\n         */\n        serial: {\n            /**\n             * @returns {Promise<Array<string>|Error>} Communication (RS232, COM, TTY) ports available on connected system.\n             *\n             * @memberof qz.serial\n             */\n            findPorts: function () {\n                return _qz.websocket.dataPromise('serial.findPorts');\n            },\n\n            /**\n             * List of functions called for any response from open serial ports.\n             * Event data will contain <code>{string} portName</code> for all types.\n             *  For RECEIVE types, <code>{string} output</code>.\n             *  For ERROR types, <code>{string} exception</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({object} streamEvent)</code> calls.\n             *\n             * @memberof qz.serial\n             */\n            setSerialCallbacks: function (calls) {\n                _qz.serial.serialCallbacks = calls;\n            },\n\n            /**\n             * Opens a serial port for sending and receiving data\n             *\n             * @param {string} port Name of serial port to open.\n             * @param {Object} [options] Serial port configurations.\n             *  @param {number} [options.baudRate=9600] Serial port speed. Set to 0 for auto negotiation.\n             *  @param {number} [options.dataBits=8] Serial port data bits. Set to 0 for auto negotiation.\n             *  @param {number} [options.stopBits=1] Serial port stop bits. Set to 0 for auto negotiation.\n             *  @param {string} [options.parity='NONE'] Serial port parity. Set to AUTO for auto negotiation. Valid values <code>[NONE | EVEN | ODD | MARK | SPACE | AUTO]</code>\n             *  @param {string} [options.flowControl='NONE'] Serial port flow control. Set to AUTO for auto negotiation. Valid values <code>[NONE | XONXOFF | XONXOFF_OUT | XONXOFF_IN | RTSCTS | RTSCTS_OUT | RTSCTS_IN | AUTO]</code>\n             *  @param {string} [options.encoding='UTF-8'] Character set for communications.\n             *  @param {string} [options.start=0x0002] DEPRECATED: Legacy character denoting start of serial response. Use <code>options.rx.start</code> instead.\n             *  @param {string} [options.end=0x000D] DEPRECATED: Legacy character denoting end of serial response. Use <code>options.rx.end</code> instead.\n             *  @param {number} [options.width] DEPRECATED: Legacy use for fixed-width response serial communication. Use <code>options.rx.width</code> instead.\n             *  @param {Object} [options.rx] Serial communications response definitions. If an object is passed but no options are defined, all response data will be sent back as it is received unprocessed.\n             *   @param {string|Array<string>} [options.rx.start] Character(s) denoting start of response bytes. Used in conjunction with `end`, `width`, or `lengthbit` property.\n             *   @param {string} [options.rx.end] Character denoting end of response bytes. Used in conjunction with `start` property.\n             *   @param {number} [options.rx.width] Fixed width size of response bytes (not including header if `start` is set). Used alone or in conjunction with `start` property.\n             *   @param {boolean} [options.rx.untilNewline] Returns data between newline characters (`\\n` or `\\r`) Truncates empty responses.  Overrides `start`, `end`, `width`.\n             *   @param {number|Object} [options.rx.lengthBytes] If a number is passed it is treated as the length index. Other values are left as their defaults.\n             *    @param {number} [options.rx.lengthBytes.index=0] Position of the response byte (not including response `start` bytes) used to denote the length of the remaining response data.\n             *    @param {number} [options.rx.lengthBytes.length=1] Length of response length bytes after response header.\n             *    @param {string} [options.rx.lengthBytes.endian='BIG'] Byte endian for multi-byte length values. Valid values <code>[BIG | LITTLE]</code>\n             *   @param {number|Object} [options.rx.crcBytes] If a number is passed it is treated as the crc length. Other values are left as their defaults.\n             *    @param {number} [options.rx.crcBytes.index=0] Position after the response data (not including length or data bytes) used to denote the crc.\n             *    @param {number} [options.rx.crcBytes.length=1] Length of response crc bytes after the response data length.\n             *   @param {boolean} [options.rx.includeHeader=false] Whether any of the header bytes (`start` bytes and any length bytes) should be included in the processed response.\n             *   @param {string} [options.rx.encoding] Override the encoding used for response data. Uses the same value as <code>options.encoding</code> otherwise.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.serial\n             */\n            openPort: function (port, options) {\n                var params = {\n                    port: port,\n                    options: options\n                };\n                return _qz.websocket.dataPromise('serial.openPort', params);\n            },\n\n            /**\n             * Send commands over a serial port.\n             * Any responses from the device will be sent to serial callback functions.\n             *\n             * @param {string} port An open serial port to send data.\n             * @param {string|Array<string>|Object} data Data to be sent to the serial device.\n             *  @param {string} [data.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             *  @param {string|Array<string>} data.data Data to be sent to the serial device.\n             * @param {Object} options Serial port configuration updates. See <code>qz.serial.openPort</code> `options` docs for available values.\n             *     For best performance, it is recommended to only set these values on the port open call.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @see qz.serial.setSerialCallbacks\n             *\n             * @memberof qz.serial\n             */\n            sendData: function (port, data, options) {\n                if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) {\n                    if (typeof data !== 'object') {\n                        data = {\n                            data: data,\n                            type: \"PLAIN\"\n                        }\n                    }\n\n                    if (data.type && data.type.toUpperCase() == \"FILE\") {\n                        data.data = _qz.tools.absolute(data.data);\n                    }\n                }\n\n                var params = {\n                    port: port,\n                    data: data,\n                    options: options\n                };\n                return _qz.websocket.dataPromise('serial.sendData', params);\n            },\n\n            /**\n             * @param {string} port Name of port to close.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.serial\n             */\n            closePort: function (port) {\n                return _qz.websocket.dataPromise('serial.closePort', { port: port });\n            }\n        },\n\n        /**\n         * Calls related to interaction with communication sockets.\n         * @namespace qz.socket\n         */\n        socket: {\n            /**\n             * Opens a network port for sending and receiving data.\n             *\n             * @param {string} host The connection hostname.\n             * @param {number} port The connection port number.\n             * @param {Object} [options] Network socket configuration.\n             *  @param {string} [options.encoding='UTF-8'] Character set for communications.\n             *\n             * @memberof qz.socket\n             */\n            open: function (host, port, options) {\n                var params = {\n                    host: host,\n                    port: port,\n                    options: options\n                };\n                return _qz.websocket.dataPromise(\"socket.open\", params);\n            },\n\n            /**\n             * @param {string} host The connection hostname.\n             * @param {number} port The connection port number.\n             *\n             * @memberof qz.socket\n             */\n            close: function (host, port) {\n                var params = {\n                    host: host,\n                    port: port\n                };\n                return _qz.websocket.dataPromise(\"socket.close\", params);\n            },\n\n            /**\n             * Send data over an open socket.\n             *\n             * @param {string} host The connection hostname.\n             * @param {number} port The connection port number.\n             * @param {string|Object} data Data to be sent over the port.\n             *  @param {string} [data.type='PLAIN'] Valid values <code>[PLAIN]</code>\n             *  @param {string} data.data Data to be sent over the port.\n             *\n             * @memberof qz.socket\n             */\n            sendData: function (host, port, data) {\n                if (typeof data !== 'object') {\n                    data = {\n                        data: data,\n                        type: \"PLAIN\"\n                    };\n                }\n\n                var params = {\n                    host: host,\n                    port: port,\n                    data: data\n                };\n                return _qz.websocket.dataPromise(\"socket.sendData\", params);\n            },\n\n            /**\n             * List of functions called for any response from open network sockets.\n             * Event data will contain <code>{string} host</code> and <code>{number} port</code> for all types.\n             *  For RECEIVE types, <code>{string} response</code>.\n             *  For ERROR types, <code>{string} exception</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             *\n             * @memberof qz.socket\n             */\n            setSocketCallbacks: function (calls) {\n                _qz.socket.socketCallbacks = calls;\n            }\n        },\n\n        /**\n         * Calls related to interaction with USB devices.\n         * @namespace qz.usb\n         */\n        usb: {\n            /**\n             * List of available USB devices. Includes (hexadecimal) vendor ID, (hexadecimal) product ID, and hub status.\n             * If supported, also returns manufacturer and product descriptions.\n             *\n             * @param includeHubs Whether to include USB hubs.\n             * @returns {Promise<Array<Object>|Error>} Array of JSON objects containing information on connected USB devices.\n             *\n             * @memberof qz.usb\n             */\n            listDevices: function (includeHubs) {\n                return _qz.websocket.dataPromise('usb.listDevices', { includeHubs: includeHubs });\n            },\n\n            /**\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             * @returns {Promise<Array<string>|Error>} List of available (hexadecimal) interfaces on a USB device.\n             *\n             * @memberof qz.usb\n             */\n            listInterfaces: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('usb.listInterfaces', deviceInfo);\n            },\n\n            /**\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.iface Hex string of interface on the USB device to search.\n             * @returns {Promise<Array<string>|Error>} List of available (hexadecimal) endpoints on a USB device's interface.\n             *\n             * @memberof qz.usb\n             */\n            listEndpoints: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        interface: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.listEndpoints', deviceInfo);\n            },\n\n            /**\n             * List of functions called for any response from open usb devices.\n             * Event data will contain <code>{string} vendorId</code> and <code>{string} productId</code> for all types.\n             *  For RECEIVE types, <code>{Array} output</code> (in hexadecimal format).\n             *  For ERROR types, <code>{string} exception</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             *\n             * @memberof qz.usb\n             */\n            setUsbCallbacks: function (calls) {\n                _qz.usb.usbCallbacks = calls;\n            },\n\n            /**\n             * Claim a USB device's interface to enable sending/reading data across an endpoint.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.interface Hex string of interface on the USB device to claim.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            claimDevice: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        interface: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.claimDevice', deviceInfo);\n            },\n\n            /**\n             * Check the current claim state of a USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             * @returns {Promise<boolean|Error>}\n             *\n             * @since 2.0.2\n             * @memberOf qz.usb\n             */\n            isClaimed: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('usb.isClaimed', deviceInfo);\n            },\n\n            /**\n             * Send data to a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             *  @param deviceInfo.data Bytes to send over specified endpoint.\n             *  @param {string} [deviceInfo.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            sendData: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2],\n                        data: arguments[3]\n                    };\n                }\n\n                if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) {\n                    if (typeof deviceInfo.data !== 'object') {\n                        deviceInfo.data = {\n                            data: deviceInfo.data,\n                            type: \"PLAIN\"\n                        }\n                    }\n\n                    if (deviceInfo.data.type && deviceInfo.data.type.toUpperCase() == \"FILE\") {\n                        deviceInfo.data.data = _qz.tools.absolute(deviceInfo.data.data);\n                    }\n                }\n\n                return _qz.websocket.dataPromise('usb.sendData', deviceInfo);\n            },\n\n            /**\n             * Read data from a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             * @returns {Promise<Array<string>|Error>} List of (hexadecimal) bytes received from the USB device.\n             *\n             * @memberof qz.usb\n             */\n            readData: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2],\n                        responseSize: arguments[3]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.readData', deviceInfo);\n            },\n\n            /**\n             * Provides a continuous stream of read data from a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             *  @param deviceInfo.interval=100 Frequency to send read data back, in milliseconds.\n             * @returns {Promise<null|Error>}\n             *\n             * @see qz.usb.setUsbCallbacks\n             *\n             * @memberof qz.usb\n             */\n            openStream: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2],\n                        responseSize: arguments[3],\n                        interval: arguments[4]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.openStream', deviceInfo);\n            },\n\n            /**\n             * Stops the stream of read data from a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            closeStream: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.closeStream', deviceInfo);\n            },\n\n            /**\n             * Release a claimed USB device to free resources after sending/reading data.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            releaseDevice: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('usb.releaseDevice', deviceInfo);\n            }\n        },\n\n\n        /**\n         * Calls related to interaction with HID USB devices<br/>\n         * Many of these calls can be accomplished from the <code>qz.usb</code> namespace,\n         * but HID allows for simpler interaction\n         * @namespace qz.hid\n         * @since 2.0.1\n         */\n        hid: {\n            /**\n             * List of available HID devices. Includes (hexadecimal) vendor ID and (hexadecimal) product ID.\n             * If available, also returns manufacturer and product descriptions.\n             *\n             * @returns {Promise<Array<Object>|Error>} Array of JSON objects containing information on connected HID devices.\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            listDevices: function () {\n                return _qz.websocket.dataPromise('hid.listDevices');\n            },\n\n            /**\n             * Start listening for HID device actions, such as attach / detach events.\n             * Reported under the ACTION type in the streamEvent on callbacks.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @see qz.hid.setHidCallbacks\n             *\n             * @memberof qz.hid\n             */\n            startListening: function () {\n                return _qz.websocket.dataPromise('hid.startListening');\n            },\n\n            /**\n             * Stop listening for HID device actions.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @see qz.hid.setHidCallbacks\n             *\n             * @memberof qz.hid\n             */\n            stopListening: function () {\n                return _qz.websocket.dataPromise('hid.stopListening');\n            },\n\n            /**\n             * List of functions called for any response from open usb devices.\n             * Event data will contain <code>{string} vendorId</code> and <code>{string} productId</code> for all types.\n             *  For RECEIVE types, <code>{Array} output</code> (in hexadecimal format).\n             *  For ERROR types, <code>{string} exception</code>.\n             *  For ACTION types, <code>{string} actionType</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            setHidCallbacks: function (calls) {\n                _qz.hid.hidCallbacks = calls;\n            },\n\n            /**\n             * Claim a HID device to enable sending/reading data across.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            claimDevice: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.claimDevice', deviceInfo);\n            },\n\n            /**\n             * Check the current claim state of a HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<boolean|Error>}\n             *\n             * @since 2.0.2\n             * @memberOf qz.hid\n             */\n            isClaimed: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.isClaimed', deviceInfo);\n            },\n\n            /**\n             * Send data to a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.data Bytes to send over specified endpoint.\n             *  @param deviceInfo.endpoint=0x00 First byte of the data packet signifying the HID report ID.\n             *                             Must be 0x00 for devices only supporting a single report.\n             *  @param deviceInfo.reportId=0x00 Alias for <code>deviceInfo.endpoint</code>. Not used if endpoint is provided.\n             *  @param {string} [deviceInfo.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            sendData: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        data: arguments[2],\n                        endpoint: arguments[3]\n                    };\n                }\n\n                if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) {\n                    if (typeof deviceInfo.data !== 'object') {\n                        deviceInfo.data = {\n                            data: deviceInfo.data,\n                            type: \"PLAIN\"\n                        }\n                    }\n\n                    if (deviceInfo.data.type && deviceInfo.data.type.toUpperCase() == \"FILE\") {\n                        deviceInfo.data.data = _qz.tools.absolute(deviceInfo.data.data);\n                    }\n                } else {\n                    if (typeof deviceInfo.data === 'object') {\n                        if (deviceInfo.data.type.toUpperCase() !== \"PLAIN\"\n                            || typeof deviceInfo.data.data !== \"string\") {\n                            return _qz.tools.reject(new Error(\"Data format is not supported with connected QZ Tray version \" + _qz.websocket.connection.version));\n                        }\n\n                        deviceInfo.data = deviceInfo.data.data;\n                    }\n                }\n\n                return _qz.websocket.dataPromise('hid.sendData', deviceInfo);\n            },\n\n            /**\n             * Read data from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             * @returns {Promise<Array<string>|Error>} List of (hexadecimal) bytes received from the HID device.\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            readData: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        responseSize: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('hid.readData', deviceInfo);\n            },\n\n            /**\n             * Send a feature report to a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.data Bytes to send over specified endpoint.\n             *  @param deviceInfo.endpoint=0x00 First byte of the data packet signifying the HID report ID.\n             *                             Must be 0x00 for devices only supporting a single report.\n             *  @param deviceInfo.reportId=0x00 Alias for <code>deviceInfo.endpoint</code>. Not used if endpoint is provided.\n             *  @param {string} [deviceInfo.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.hid\n             */\n            sendFeatureReport: function (deviceInfo) {\n                return _qz.websocket.dataPromise('hid.sendFeatureReport', deviceInfo);\n            },\n\n            /**\n             * Get a feature report from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             * @returns {Promise<Array<string>|Error>} List of (hexadecimal) bytes received from the HID device.\n             *\n             * @memberof qz.hid\n             */\n            getFeatureReport: function (deviceInfo) {\n                return _qz.websocket.dataPromise('hid.getFeatureReport', deviceInfo);\n            },\n\n            /**\n             * Provides a continuous stream of read data from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             *  @param deviceInfo.interval=100 Frequency to send read data back, in milliseconds.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @see qz.hid.setHidCallbacks\n             *\n             * @memberof qz.hid\n             */\n            openStream: function (deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        responseSize: arguments[2],\n                        interval: arguments[3]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('hid.openStream', deviceInfo);\n            },\n\n            /**\n             * Stops the stream of read data from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            closeStream: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.closeStream', deviceInfo);\n            },\n\n            /**\n             * Release a claimed HID device to free resources after sending/reading data.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            releaseDevice: function (deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.releaseDevice', deviceInfo);\n            }\n        },\n\n\n        /**\n         * Calls related to interactions with the filesystem\n         * @namespace qz.file\n         * @since 2.1\n         */\n        file: {\n            /**\n             * List of files available at the given directory.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute directory path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             * @returns {Promise<Array<String>|Error>} Array of files at the given path\n             *\n             * @memberof qz.file\n             */\n            list: function (path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.list', param);\n            },\n\n            /**\n             * Reads contents of file at the given path.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             *  @param {string} [params.flavor='plain'] Flavor of data format used, valid flavors are <code>[base64 | hex | plain]</code>.\n             * @returns {Promise<String|Error>} String containing the file contents\n             *\n             * @memberof qz.file\n             */\n            read: function (path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.read', param);\n            },\n\n            /**\n             * Writes data to the file at the given path.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} params Object containing file access parameters\n             *  @param {string} params.data File data to be written\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             *  @param {boolean} [params.append=false] Appends to the end of the file if set, otherwise overwrites existing contents\n             *  @param {string} [params.flavor='plain'] Flavor of data format used, valid flavors are <code>[base64 | file | hex | plain]</code>.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.file\n             */\n            write: function (path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.write', param);\n            },\n\n            /**\n             * Deletes a file at given path.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.file\n             */\n            remove: function (path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.remove', param);\n            },\n\n            /**\n             * Provides a continuous stream of events (and optionally data) from a local file.\n             *\n             * @param {string} path Relative or absolute directory path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             *  @param {Object} [params.listener] If defined, file data will be returned on events\n             *   @param {number} [params.listener.bytes=-1] Number of bytes to return or -1 for all\n             *   @param {number} [params.listener.lines=-1] Number of lines to return or -1 for all\n             *   @param {boolean} [params.listener.reverse] Controls whether data should be returned from the bottom of the file.  Default value is true for line mode and false for byte mode.\n             *   @param {string|Array<string>} [params.include] File patterns to match.  Blank values will be ignored.\n             *   @param {string|Array<string>} [params.exclude] File patterns to exclude.  Blank values will be ignored.  Takes priority over <code>params.include</code>.\n             *   @param {boolean} [params.ignoreCase=true] Whether <code>params.include</code> or <code>params.exclude</code> are case-sensitive.\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.file.setFileCallbacks\n             *\n             * @memberof qz.file\n             */\n            startListening: function (path, params) {\n                if (params && typeof params.include !== 'undefined' && !Array.isArray(params.include)) {\n                    params.include = [params.include];\n                }\n                if (params && typeof params.exclude !== 'undefined' && !Array.isArray(params.exclude)) {\n                    params.exclude = [params.exclude];\n                }\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.startListening', param);\n            },\n\n            /**\n             * Closes listeners with the provided settings. Omitting the path parameter will result in all listeners closing.\n             *\n             * @param {string} [path] Previously opened directory path of listener to close, or omit to close all.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.file\n             */\n            stopListening: function (path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.stopListening', param);\n            },\n\n            /**\n             * List of functions called for any response from a file listener.\n             *  For ERROR types event data will contain, <code>{string} message</code>.\n             *  For ACTION types event data will contain, <code>{string} file {string} eventType {string} [data]</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             * @since 2.1.0\n             *\n             * @memberof qz.file\n             */\n            setFileCallbacks: function (calls) {\n                _qz.file.fileCallbacks = calls;\n            }\n        },\n\n        /**\n         * Calls related to networking information\n         * @namespace qz.networking\n         * @since 2.1.0\n         */\n        networking: {\n            /**\n             * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] Port to use with custom hostname, defaults to 443\n             * @returns {Promise<Object|Error>} Connected system's network information.\n             *\n             * @memberof qz.networking\n             * @since 2.1.0\n             */\n            device: function (hostname, port) {\n                // Wrap 2.0\n                if (_qz.tools.isVersion(2, 0)) {\n                    return _qz.compatible.networking(hostname, port, null, null, function (data) {\n                        return { ip: data.ipAddress, mac: data.macAddress };\n                    });\n                }\n                // Use 2.1\n                return _qz.websocket.dataPromise('networking.device', {\n                    hostname: hostname,\n                    port: port\n                });\n            },\n\n            /**\n             * Get computer hostname\n             *\n             * @param {string} [hostname] DEPRECATED Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] DEPRECATED Port to use with custom hostname, defaults to 443\n             * @returns {Promise<string|Error>} Connected system's hostname.\n             *\n             * @memberof qz.networking\n             * @since 2.2.2\n             */\n            hostname: function (hostname, port) {\n                // Wrap < 2.2.2\n                if (_qz.tools.versionCompare(2, 2, 2) < 0) {\n                    return _qz.tools.promise(function (resolve, reject) {\n                        _qz.websocket.dataPromise('networking.device', { hostname: hostname, port: port }).then(function (device) {\n                            console.log(device);\n                            resolve(device.hostname);\n                        });\n                    });\n                } else {\n                    return _qz.websocket.dataPromise('networking.hostname');\n                }\n            },\n\n            /**\n             * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] Port to use with custom hostname, defaults to 443\n             * @returns {Promise<Array<Object>|Error>} Connected system's network information.\n             *\n             * @memberof qz.networking\n             * @since 2.1.0\n             */\n            devices: function (hostname, port) {\n                // Wrap 2.0\n                if (_qz.tools.isVersion(2, 0)) {\n                    return _qz.compatible.networking(hostname, port, null, null, function (data) {\n                        return [{ ip: data.ipAddress, mac: data.macAddress }];\n                    });\n                }\n                // Use 2.1\n                return _qz.websocket.dataPromise('networking.devices', {\n                    hostname: hostname,\n                    port: port\n                });\n            }\n        },\n\n\n        /**\n         * Calls related to signing connection requests.\n         * @namespace qz.security\n         */\n        security: {\n            /**\n             * Set promise resolver for calls to acquire the site's certificate.\n             *\n             * @param {Function|AsyncFunction|Promise<string>} promiseHandler Either a function that will be used as a promise resolver (of format <code>Function({function} resolve, {function}reject)</code>),\n             *     an async function, or a promise. Any of which should return the public certificate via their respective <code>resolve</code> call.\n             *\n             * @memberof qz.security\n             */\n            setCertificatePromise: function (promiseHandler) {\n                _qz.security.certHandler = promiseHandler;\n            },\n\n            /**\n             * Set promise factory for calls to sign API calls.\n             *\n             * @param {Function|AsyncFunction} promiseFactory Either a function that accepts a string parameter of the data to be signed\n             *     and returns a function to be used as a promise resolver (of format <code>Function({function} resolve, {function}reject)</code>),\n             *     or an async function that can take a string parameter of the data to be signed. Either of which should return the signed contents of\n             *     the passed string parameter via their respective <code>resolve</code> call.\n             *\n             * @example\n             *  qz.security.setSignaturePromise(function(dataToSign) {\n             *    return function(resolve, reject) {\n             *      $.ajax(\"/signing-url?data=\" + dataToSign).then(resolve, reject);\n             *    }\n             *  })\n             *\n             * @memberof qz.security\n             */\n            setSignaturePromise: function (promiseFactory) {\n                _qz.security.signatureFactory = promiseFactory;\n            },\n\n            /**\n             * Set which signing algorithm QZ will check signatures against.\n             *\n             * @param {string} algorithm The algorithm used in signing. Valid values: <code>[SHA1 | SHA256 | SHA512]</code>\n             * @since 2.1.0\n             *\n             * @memberof qz.security\n             */\n            setSignatureAlgorithm: function (algorithm) {\n                //warn for incompatibilities if known\n                if (!_qz.compatible.algorithm()) {\n                    return;\n                }\n\n                if ([\"SHA1\", \"SHA256\", \"SHA512\"].indexOf(algorithm.toUpperCase()) < 0) {\n                    _qz.log.error(\"Signing algorithm '\" + algorithm + \"' is not supported.\");\n                } else {\n                    _qz.security.signAlgorithm = algorithm;\n                }\n            },\n\n            /**\n             * Get the signing algorithm QZ will be checking signatures against.\n             *\n             * @returns {string} The algorithm used in signing.\n             * @since 2.1.0\n             *\n             * @memberof qz.security\n             */\n            getSignatureAlgorithm: function () {\n                return _qz.security.signAlgorithm;\n            }\n        },\n\n        /**\n         * Calls related to compatibility adjustments\n         * @namespace qz.api\n         */\n        api: {\n            /**\n             * Show or hide QZ api debugging statements in the browser console.\n             *\n             * @param {boolean} show Whether the debugging logs for QZ should be shown. Hidden by default.\n             * @returns {boolean} Value of debugging flag\n             * @memberof qz.api\n             */\n            showDebug: function (show) {\n                return (_qz.DEBUG = show);\n            },\n\n            /**\n             * Get version of connected QZ Tray application.\n             *\n             * @returns {Promise<string|Error>} Version number of QZ Tray.\n             *\n             * @memberof qz.api\n             */\n            getVersion: function () {\n                return _qz.websocket.dataPromise('getVersion');\n            },\n\n            /**\n             * Checks for the specified version of connected QZ Tray application.\n             *\n             * @param {string|number} [major] Major version to check\n             * @param {string|number} [minor] Minor version to check\n             * @param {string|number} [patch] Patch version to check\n             *\n             * @memberof qz.api\n             */\n            isVersion: _qz.tools.isVersion,\n\n            /**\n             * Checks if the connected QZ Tray application is greater than the specified version.\n             *\n             * @param {string|number} major Major version to check\n             * @param {string|number} [minor] Minor version to check\n             * @param {string|number} [patch] Patch version to check\n             * @param {string|number} [build] Build version to check\n             * @returns {boolean} True if connected version is greater than the version specified.\n             *\n             * @memberof qz.api\n             * @since 2.1.0-4\n             */\n            isVersionGreater: function (major, minor, patch, build) {\n                return _qz.tools.versionCompare(major, minor, patch, build) > 0;\n            },\n\n            /**\n             * Checks if the connected QZ Tray application is less than the specified version.\n             *\n             * @param {string|number} major Major version to check\n             * @param {string|number} [minor] Minor version to check\n             * @param {string|number} [patch] Patch version to check\n             * @param {string|number} [build] Build version to check\n             * @returns {boolean} True if connected version is less than the version specified.\n             *\n             * @memberof qz.api\n             * @since 2.1.0-4\n             */\n            isVersionLess: function (major, minor, patch, build) {\n                return _qz.tools.versionCompare(major, minor, patch, build) < 0;\n            },\n\n            /**\n             * Change the promise library used by QZ API.\n             * Should be called before any initialization to avoid possible errors.\n             *\n             * @param {Function} promiser <code>Function({function} resolver)</code> called to create new promises.\n             *\n             * @memberof qz.api\n             */\n            setPromiseType: function (promiser) {\n                _qz.tools.promise = promiser;\n            },\n\n            /**\n             * Change the SHA-256 hashing function used by QZ API.\n             * Should be called before any initialization to avoid possible errors.\n             *\n             * @param {Function} hasher <code>Function({function} message)</code> called to create hash of passed string.\n             *\n             * @memberof qz.api\n             */\n            setSha256Type: function (hasher) {\n                _qz.tools.hash = hasher;\n            },\n\n            /**\n             * Change the WebSocket handler.\n             * Should be called before any initialization to avoid possible errors.\n             *\n             * @param {Function} ws <code>Function({function} WebSocket)</code> called to override the internal WebSocket handler.\n             *\n             * @memberof qz.api\n             */\n            setWebSocketType: function (ws) {\n                _qz.tools.ws = ws;\n            }\n        },\n\n        /**\n         * Version of this JavaScript library\n         *\n         * @constant {string}\n         *\n         * @memberof qz\n         */\n        version: _qz.VERSION\n    };\n\n    return qz;\n})();\n\n\n(function () {\n    if (typeof define === 'function' && define.amd) {\n        define(qz);\n    } else if (typeof exports === 'object') {\n        module.exports = qz;\n    } else {\n        window.qz = qz;\n    }\n})();"
  },
  {
    "path": "ury/public/js/restrict_qty_edit_pos.js",
    "content": "frappe.ui.form.on('POS Invoice', {\n    async onload(frm) {\n        const fieldValue = await frappe.db.get_value('POS Profile', { 'name': frm.doc.pos_profile }, 'remove_items');\n\n        if (fieldValue.message.remove_items === 0) {\n\n            const items_details_section = document.querySelector(\".item-details-container\");\n            const item_observer = new MutationObserver(() => {\n                const computedStyle = window.getComputedStyle(items_details_section);\n                if (computedStyle.display === \"flex\") {\n                    const fields = ['uom', 'warehouse'];\n                    for (const field of fields) {\n                        $(`input[type=\"text\"][data-fieldname=\"${field}\"]`).addClass('like-disabled-input').removeClass('bold').css('pointer-events', 'none')\n                    }\n                    console.log(cur_frm.selected_doc.invoice_printed, \"Selected\")\n                    if (cur_frm.doc.restaurant_table && cur_frm.selected_doc.invoice_printed == 1) {\n                        $('input[type=\"text\"][data-fieldname=\"qty\"]').addClass('like-disabled-input').removeClass('bold').css('pointer-events', 'none');\n                        document.querySelector('div[class=\"numpad-btn col-span-2 remove-btn\"][data-button-value=\"remove\"]').disabled = true;\n                        document.querySelector('div[class=\"numpad-btn col-span-2\"][data-button-value=\"qty\"]').disabled = true;\n                        document.querySelector('div[class=\"numpad-btn \"][data-button-value=\"delete\"]').disabled = true;\n                    }\n                }\n                $(\".item-details-container\").click(function () {\n                    const fields = ['uom', 'warehouse'];\n                    for (const field of fields) {\n                        $(`input[type=\"text\"][data-fieldname=\"${field}\"]`).addClass('like-disabled-input').removeClass('bold').css('pointer-events', 'none')\n                    }\n                    if (cur_frm.doc.restaurant_table && cur_frm.selected_doc.invoice_printed == 1) {\n                        $('input[type=\"text\"][data-fieldname=\"qty\"]').addClass('like-disabled-input').removeClass('bold').css('pointer-events', 'none');\n                    }\n                })\n                $(\".customer-cart-container\").click(function () {\n                    const fields = ['uom', 'warehouse'];\n                    for (const field of fields) {\n                        $(`input[type=\"text\"][data-fieldname=\"${field}\"]`).addClass('like-disabled-input').removeClass('bold').css('pointer-events', 'none')\n                    }\n                    if (cur_frm.doc.restaurant_table && cur_frm.selected_doc.invoice_printed == 1) {\n                        $('input[type=\"text\"][data-fieldname=\"qty\"]').addClass('like-disabled-input').removeClass('bold').css('pointer-events', 'none');\n                    }\n                })\n            });\n            item_observer.observe(items_details_section, { attributes: true });\n        }\n    }\n});\n"
  },
  {
    "path": "ury/public/js/sign-message.js",
    "content": "/*\n * JavaScript client-side example using jsrsasign\n */\n\n// #########################################################\n// #             WARNING   WARNING   WARNING               #\n// #########################################################\n// #                                                       #\n// # This file is intended for demonstration purposes      #\n// # only.                                                 #\n// #                                                       #\n// # It is the SOLE responsibility of YOU, the programmer  #\n// # to prevent against unauthorized access to any signing #\n// # functions.                                            #\n// #                                                       #\n// # Organizations that do not protect against un-         #\n// # authorized signing will be black-listed to prevent    #\n// # software piracy.                                      #\n// #                                                       #\n// # -QZ Industries, LLC                                   #\n// #                                                       #\n// #########################################################\n\n/**\n * Depends:\n *     - jsrsasign-latest-all-min.js\n *     - qz-tray.js\n *\n * Steps:\n *\n *     1. Include jsrsasign 8.0.4 into your web page\n *        <script src=\"https://cdn.rawgit.com/kjur/jsrsasign/c057d3447b194fa0a3fdcea110579454898e093d/jsrsasign-all-min.js\"></script>\n *\n *     2. Update the privateKey below with contents from private-key.pem\n *\n *     3. Include this script into your web page\n *        <script src=\"path/to/sign-message.js\"></script>\n *\n *     4. Remove or comment out any other references to \"setSignaturePromise\"\n */\n\n\n// Include the necessary libraries\nvar qzPrivateKey = null; // Initialize the variable to hold the private key\n\n// Load the private key path from the site config.json using Frappe API\nfrappe.call({\n    method: 'ury.ury.api.ury_print.signature_promise',\n    callback: function (response) {\n        if (response.message) {\n            var privateKeyPath = response.message;\n            // Load the private key from the specified path asynchronously\n            fetch(\"/private/\" + privateKeyPath)\n                .then(response => response.text())\n                .then(privateKey => {\n                    qzPrivateKey = privateKey; // Store the private key in the variable\n\n                    // Set the signature algorithm and function\n                    qz.security.setSignatureAlgorithm(\"SHA512\"); // Since 2.1\n                    qz.security.setSignaturePromise(function (toSign) {\n                        return function (resolve, reject) {\n                            try {\n                                var pk = KEYUTIL.getKey(qzPrivateKey);\n                                var sig = new KJUR.crypto.Signature({ \"alg\": \"SHA512withRSA\" });  // Use \"SHA1withRSA\" for QZ Tray 2.0 and older\n                                sig.init(pk);\n                                sig.updateString(toSign);\n                                var hex = sig.sign();\n                                resolve(stob64(hextorstr(hex)));\n                            } catch (err) {\n                                console.error(err);\n                                reject(err);\n                            }\n                        };\n                    });\n\n                    // Continue with the rest of your code\n                })\n                .catch(error => {\n                    console.error(\"Failed to load the private key:\", error);\n                });\n        }\n    }\n});\n"
  },
  {
    "path": "ury/public/js/ury_pos_kot.js",
    "content": "let old_items = [];\nlet new_items = [];\nlet finalarray = [];\nfrappe.ui.form.on(\"POS Invoice\", {\n  refresh: function (frm) {\n    cur_frm.check = true;\n  },\n  after_save: function (frm) {\n    let invoice_comment = cur_frm.order_comments;\n\n    if (cur_frm.check == true) {\n      old_items = cur_frm.old_items;\n    }\n    let newItems = frm.doc.items;\n    let invoice_id = frm.doc.name;\n    newItems.forEach((new_item) => {\n      let json_newitem = {\n        item_code: new_item.item_code,\n        qty: new_item.qty,\n        item_name: new_item.item_name,\n        name: new_item.name,\n        comments: \"\",\n      };\n      new_items.push(json_newitem);\n    });\n\n    frm.call({\n      method: \"ury.ury.api.ury_kot_generate.kot_execute\",\n      args: {\n        invoice_id: invoice_id,\n        customer: frm.doc.customer,\n        current_items: new_items,\n        previous_items: old_items,\n        comments: invoice_comment,\n      },\n      callback: function (r) {\n        cur_frm.order_comments = \"\";\n\n        old_items = new_items;\n\n        new_items = [];\n        cur_frm.check = false;\n\n        frappe.show_alert({ message: __(\"Order Updated\"), indicator: \"green\" });\n      },\n    });\n  },\n});\n"
  },
  {
    "path": "ury/setup.py",
    "content": "import frappe\nimport os\nimport click\nfrom frappe import _\n\nfrom frappe.custom.doctype.custom_field.custom_field import create_custom_fields\n\ndef after_install():\n    create_custom_fields(get_custom_fields())\n    \ndef before_uninstall():\n\tdelete_custom_fields(get_custom_fields())\n \ndef get_custom_fields():\n\t\"\"\"URY specific custom fields that need to be added to the masters in ERPNext\"\"\"\n\treturn {\n     \t\"POS Invoice\": [\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"mobile_number\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"fetch_from\": \"customer.mobile_number\",\n\t\t\t\t\t\"label\": \"Mobile Number\",\n\t\t\t\t\t\"insert_after\": \"customer_name\",\n\t\t\t\t\t\"translatable\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"order_info\",\n\t\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\t\"label\": \"Order Info\",\n\t\t\t\t\t\"insert_after\": \"return_against\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"order_type\",\n\t\t\t\t\t\"fieldtype\": \"Select\",\n\t\t\t\t\t\"default\": \"Dine In\",\n\t\t\t\t\t\"label\": \"Order Type\",\n\t\t\t\t\t\"options\": \"\\nDine In\\nTake Away\\nDelivery\\nPhone In\\nAggregators\",\n\t\t\t\t\t\"insert_after\": \"order_info\",\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"waiter\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"label\": \"Waiter\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"insert_after\": \"order_type\",\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"column_break_rwbwf\",\n\t\t\t\t\t\"fieldtype\": \"Column Break\",\n\t\t\t\t\t\"insert_after\": \"waiter\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"no_of_pax\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"label\": \"Pax\",\n\t\t\t\t\t\"insert_after\": \"column_break_rwbwf\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"cashier\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"label\": \"Cashier\",\n\t\t\t\t\t\"insert_after\": \"no_of_pax\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"invoice_printed\",\n\t\t\t\t\t\"fieldtype\": \"Check\",\n\t\t\t\t\t\"label\": \"Invoice Printed\",\n\t\t\t\t\t\"insert_after\": \"cashier\",\n\t\t\t\t\t\"read_only\": 1,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"invoice_created\",\n\t\t\t\t\t\"fieldtype\": \"Check\",\n\t\t\t\t\t\"label\": \"Invoice Created\",\n\t\t\t\t\t\"insert_after\": \"invoice_printed\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"hidden\": 1,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"restaurant_info\",\n\t\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\t\"label\": \"Restaurant Info\",\n\t\t\t\t\t\"insert_after\": \"invoice_created\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"restaurant\",\n\t\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\t\"insert_after\": \"restaurant_info\",\n\t\t\t\t\t\"label\": \"Restaurant\",\n\t\t\t\t\t\"options\": \"URY Restaurant\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"branch\",\n\t\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\t\"insert_after\": \"restaurant\",\n\t\t\t\t\t\"label\": \"Branch\",\n\t\t\t\t\t\"options\": \"Branch\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"restaurant_table\",\n\t\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\t\"insert_after\": \"branch\",\n\t\t\t\t\t\"label\": \"Restaurant Table\",\n\t\t\t\t\t\"options\": \"URY Table\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"column_break_gd1mq\",\n\t\t\t\t\t\"fieldtype\": \"Column Break\",\n\t\t\t\t\t\"insert_after\": \"restaurant_table\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"arrived_time\",\n\t\t\t\t\t\"fieldtype\": \"Time\",\n\t\t\t\t\t\"insert_after\": \"column_break_gd1mq\",\n\t\t\t\t\t\"label\": \"Arrived Time\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"total_spend_time\",\n\t\t\t\t\t\"fieldtype\": \"Time\",\n\t\t\t\t\t\"insert_after\": \"arrived_time\",\n\t\t\t\t\t\"label\": \"Total Spend Time\"\n\t\t\t\t}\n\t\t\t\t],\n      \n\t\t\"Sales Invoice\": [\n\t\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"mobile_number\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"fetch_from\": \"customer.mobile_number\",\n\t\t\t\t\t\"label\": \"Mobile Number\",\n\t\t\t\t\t\"insert_after\": \"customer_name\",\n\t\t\t\t\t\"translatable\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"order_info\",\n\t\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\t\"label\": \"Order Info\",\n\t\t\t\t\t\"insert_after\": \"return_against\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"order_type\",\n\t\t\t\t\t\"fieldtype\": \"Select\",\n\t\t\t\t\t\"default\": \"Dine In\",\n\t\t\t\t\t\"options\": \"URY Restaurant\",\n\t\t\t\t\t\"fetch_from\": \"customer.mobile_number\",\n\t\t\t\t\t\"label\": \"Order Type\",\n\t\t\t\t\t\"options\": \"\\nDine In\\nTake Away\\nDelivery\\nPhone In\\nAggregators\",\n\t\t\t\t\t\"insert_after\": \"order_info\",\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"waiter\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"label\": \"Waiter\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"insert_after\": \"order_type\",\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"column_break_rwbwf\",\n\t\t\t\t\t\"fieldtype\": \"Column Break\",\n\t\t\t\t\t\"insert_after\": \"waiter\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"no_of_pax\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"label\": \"Pax\",\n\t\t\t\t\t\"insert_after\": \"column_break_rwbwf\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"cashier\",\n\t\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\t\"label\": \"Cashier\",\n\t\t\t\t\t\"insert_after\": \"no_of_pax\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t\t\"translatable\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"restaurant_info\",\n\t\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\t\"label\": \"Restaurant Info\",\n\t\t\t\t\t\"insert_after\": \"invoice_created\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"restaurant\",\n\t\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\t\"insert_after\": \"restaurant_info\",\n\t\t\t\t\t\"label\": \"Restaurant\",\n\t\t\t\t\t\"options\": \"URY Restaurant\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"branch\",\n\t\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\t\"insert_after\": \"restaurant\",\n\t\t\t\t\t\"label\": \"Branch\",\n\t\t\t\t\t\"options\": \"Branch\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"restaurant_table\",\n\t\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\t\"insert_after\": \"branch\",\n\t\t\t\t\t\"label\": \"Restaurant Table\",\n\t\t\t\t\t\"options\": \"URY Table\",\n\t\t\t\t\t\"read_only\": 0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"column_break_gd1mq\",\n\t\t\t\t\t\"fieldtype\": \"Column Break\",\n\t\t\t\t\t\"insert_after\": \"restaurant_table\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"arrived_time\",\n\t\t\t\t\t\"fieldtype\": \"Time\",\n\t\t\t\t\t\"insert_after\": \"column_break_gd1mq\",\n\t\t\t\t\t\"label\": \"Arrived Time\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fieldname\": \"total_spend_time\",\n\t\t\t\t\t\"fieldtype\": \"Time\",\n\t\t\t\t\t\"insert_after\": \"arrived_time\",\n\t\t\t\t\t\"label\": \"Total Spend Time\"\n\t\t\t\t}\n\t\t\t\t],\n\n\t\t\"POS Profile\": [\n\t\t\t{\n\t\t\t\t\"fieldname\": \"restaurant_info\",\n\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\"label\": \"Restaurant Info\",\n\t\t\t\t\"insert_after\": \"company_address\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fieldname\": \"restaurant\",\n\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\"insert_after\": \"restaurant_info\",\n\t\t\t\t\"label\": \"Restaurant\",\n\t\t\t\t\"options\": \"URY Restaurant\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fieldname\": \"column_break_c10ag\",\n\t\t\t\t\"fieldtype\": \"Column Break\",\n\t\t\t\t\"insert_after\": \"restaurant\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fetch_from\": \"restaurant.branch\" ,\n\t\t\t\t\"fieldname\": \"branch\",\n\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\"insert_after\": \"column_break_c10ag\",\n\t\t\t\t\"label\": \"Branch\",\n\t\t\t\t\"options\": \"Branch\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fieldname\": \"printer_info\",\n\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\"label\": \"Printer Info\",\n\t\t\t\t\"insert_after\": \"branch\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"depends_on\": \"eval:doc.qz_print != 1\" , \n\t\t\t\t\"fieldname\": \"printer_settings\",\n\t\t\t\t\"fieldtype\": \"Table\",\n\t\t\t\t\"insert_after\": \"printer_info\",\n\t\t\t\t\"label\": \"Printer Settings\",\n\t\t\t\t\"options\": \"URY Printer Settings\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fieldname\": \"qz_print\",\n\t\t\t\t\"fieldtype\": \"Check\",\n\t\t\t\t\"label\": \"QZ Print\",\n\t\t\t\t\"insert_after\": \"printer_settings\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"depends_on\": \"qz_print\",\n\t\t\t\t\"fieldname\": \"qz_host\",\n\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\"insert_after\": \"qz_print\",\n\t\t\t\t\"label\": \"QZ Host\",\n\t\t\t\t\"translatable\": 0,\n\t\t\t}\n\t\t],\n  \n\t\t\"POS Opening Entry\": [\n\t\t\t{\n\t\t\t\t\"fieldname\": \"restaurant_info\",\n\t\t\t\t\"fieldtype\": \"Section Break\",\n\t\t\t\t\"label\": \"Restaurant Info\",\n\t\t\t\t\"insert_after\": \"user\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fieldname\": \"restaurant\",\n\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\"insert_after\": \"restaurant_info\",\n\t\t\t\t\"label\": \"Restaurant\",\n\t\t\t\t\"options\": \"URY Restaurant\",\n\t\t\t\t\"reqd\": 1\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"fieldname\": \"column_break_e3dky\",\n\t\t\t\t\"fieldtype\": \"Column Break\",\n\t\t\t\t\"insert_after\": \"restaurant\",\n\t\t\t},\n\t\t\t{\t\n\t\t\t\t\"fieldname\": \"branch\",\n\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\"insert_after\": \"column_break_e3dky\",\n\t\t\t\t\"label\": \"Branch\",\n\t\t\t\t\"options\": \"Branch\",\n\t\t\t\t\"reqd\": 1\n\t\t\t}\n\t\t],\n\n\t\t\"Price List\": [\n\t\t\t{\n\t\t\t\t\"fieldname\": \"restaurant_menu\",\n\t\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\t\"options\": \"URY Menu\",\n\t\t\t\t\"label\": \"Restaurant Menu\",\n\t\t\t\t\"insert_after\": \"currency\",\n\t\t\t}\n\t\t],\n  \n\t\t\"Branch\": [\n\t\t\t{\n\t\t\t\t\"fieldname\": \"user\",\n\t\t\t\t\"fieldtype\": \"Table\",\n\t\t\t\t\"options\": \"URY User\",\n\t\t\t\t\"label\": \"User\",\n\t\t\t\t\"insert_after\": \"branch\",\n\t\t\t\t\"reqd\": 1\n\t\t\t}\n\t\t],\n\n\t\t\"Customer\": [\n\t\t\t{\n\t\t\t\t\"fieldname\": \"mobile_number\",\n\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\"label\": \"Mobile Number\",\n\t\t\t\t\"insert_after\": \"customer_name\",\n\t\t\t\t\"translatable\": 0,\n\t\t\t\t\"reqd\": 1\n\t\t\t},\n\t\t],\n\n\t\t\"POS Invoice Iten\": [\n\t\t\t{\n\t\t\t\t\"fieldname\": \"comment\",\n\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\"label\": \"Comment\",\n\t\t\t\t\"insert_after\": \"description\",\n\t\t\t\t\"translatable\": 0\n\t\t\t}\n\t\t],\n     \n    }\n \ndef delete_custom_fields(custom_fields):\n    for doctype, fields in custom_fields.items():\n        frappe.db.delete(\n\t\t\t\"Custom Field\",\n\t\t\t{\n\t\t\t\t\"fieldname\": (\"in\", [field[\"fieldname\"] for field in fields]),\n\t\t\t\t\"dt\": doctype,\n\t\t\t},\n\t\t)\n        \n        frappe.clear_cache(doctype=doctype)\n \n \n    \n"
  },
  {
    "path": "ury/templates/__init__.py",
    "content": ""
  },
  {
    "path": "ury/templates/pages/__init__.py",
    "content": ""
  },
  {
    "path": "ury/uninstall.py",
    "content": "import click\nimport frappe\n\nfrom ury.setup import before_uninstall as remove_custom_fields\n\n\ndef before_uninstall():\n    try:\n        print(\"Removing customizations created by the Frappe URY app...\")\n        # remove_custom_fields()\n\n    except:\n        print(\"Failed To Remove Customizations.\")\n\ndef uninstall():\n\tROLES = [\"URY Manager\", \"URY Captain\", \"URY Cashier\"]\n\n\tfrappe.db.delete(\"Custom DocPerm\",{\"role\": [\"in\", ROLES]})\n\n\tprint (\"* removing URY Roles...\")\n\tfrappe.db.delete(\"Role\", {\"name\": [\"in\", ROLES]})"
  },
  {
    "path": "ury/ury/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/api/button_permission.py",
    "content": "import frappe\n\n\n@frappe.whitelist()\ndef cancel_check():\n    if frappe.permissions.has_permission(\n        \"POS Invoice\", \"cancel\", raise_exception=False\n    ):\n        permission = True\n    else:\n        permission = False\n    return permission\n"
  },
  {
    "path": "ury/ury/api/pos_extend.py",
    "content": "import frappe\nfrom frappe import _\n\ndef validate_search_input(search_term):\n    \"\"\"Validate and sanitize search input\"\"\"\n    if not search_term:\n        return \"\"\n\n    # Length validation\n    if len(search_term) > 100:\n        frappe.throw(_(\"Search term too long (max 100 characters)\"))\n\n    # Character whitelist (adjust based on requirements)\n    import re\n    if not re.match(r'^[a-zA-Z0-9\\s\\-_@.]+$', search_term):\n        frappe.throw(_(\"Invalid characters in search term\"))\n\n    return search_term\n\n@frappe.whitelist()\ndef overrided_past_order_list(search_term, status, limit=20):\n    user = frappe.session.user\n    search_term = validate_search_input(search_term)\n    if user != \"Administrator\":\n        sql_query = \"\"\"\n            SELECT b.branch,a.room\n            FROM `tabURY User` AS a\n            INNER JOIN `tabBranch` AS b ON a.parent = b.name\n            WHERE a.user = %s\n        \"\"\"\n        branch_array = frappe.db.sql(sql_query, user, as_dict=True)\n\n        if not branch_array:\n            frappe.throw(\"User is not Associated with any Branch.Please refresh Page\")\n\n        branch_name = branch_array[0].get(\"branch\")\n        room_name = branch_array[0].get(\"room\")\n\n    fields = [\n        \"name\",\n        \"grand_total\",\n        \"currency\",\n        \"customer\",\n        \"posting_time\",\n        \"posting_date\",\n        \"restaurant_table\",\n        \"invoice_printed\",\n    ]\n    invoice_list = []\n    updated_list = []\n\n    if search_term and status:\n        invoices_by_customer = frappe.db.get_all(\n            \"POS Invoice\",\n            filters={\n                \"customer\": [\"like\", \"%{}%\".format(frappe.db.escape(search_term))],\n                \"status\": status,\n            },\n            fields=fields,\n        )\n        invoices_by_name = frappe.db.get_all(\n            \"POS Invoice\",\n            filters={\"name\": [\"like\", \"%{}%\".format(frappe.db.escape(search_term))], \"status\": status},\n            fields=fields,\n        )\n        print(\"invoices by customer\",invoices_by_customer)\n        invoice_list = invoices_by_customer + invoices_by_name\n        updated_list = invoice_list\n    elif status:\n        if user != \"Administrator\":\n            if status == \"To Bill\":\n                invoice_list = frappe.db.get_all(\n                    \"POS Invoice\",\n                    filters={\"status\": \"Draft\", \"branch\": branch_name,\"custom_restaurant_room\": room_name},\n                    fields=fields,\n                )\n                for invoice in invoice_list:\n                    if invoice.restaurant_table and invoice.invoice_printed == 0:\n                        updated_list.append(invoice)\n\n            else:\n                invoice_list = frappe.db.get_all(\n                    \"POS Invoice\",\n                    filters={\"status\": status, \"branch\": branch_name,\"custom_restaurant_room\":room_name},\n                    fields=fields,\n                )\n                for invoice in invoice_list:\n                    if not invoice.restaurant_table or invoice.invoice_printed == 1:\n                        updated_list.append(invoice)\n\n        else:\n            if status == \"To Bill\":\n                invoice_list = frappe.db.get_all(\n                    \"POS Invoice\",\n                    filters={\"status\": \"Draft\"},\n                    fields=fields,\n                )\n                for invoice in invoice_list:\n                    if invoice.restaurant_table and invoice.invoice_printed == 0:\n                        updated_list.append(invoice)\n\n            else:\n                invoice_list = frappe.db.get_all(\n                    \"POS Invoice\",\n                    filters={\"status\": status},\n                    fields=fields,\n                )\n                for invoice in invoice_list:\n                    if not invoice.restaurant_table or invoice.invoice_printed == 1:\n                        updated_list.append(invoice)\n\n    return updated_list\n"
  },
  {
    "path": "ury/ury/api/ury_kot_display.py",
    "content": "import json\n\nimport frappe\nfrom ury.ury_pos.api import getBranch\nfrom frappe.utils import get_datetime\n\n\n# Function to set order status in a KOT document\n@frappe.whitelist()\ndef serve_kot(name, time):\n    current_time = get_datetime()\n    creation_time = frappe.db.get_value(\"URY KOT\",name,\"creation\")\n\n    production_time = current_time - creation_time\n    production_time_minutes = production_time.total_seconds() / 60\n    frappe.db.set_value(\"URY KOT\", name, \"start_time_serv\", time)\n    frappe.db.set_value(\"URY KOT\",name,\"production_time\",production_time_minutes)\n    frappe.db.set_value(\"URY KOT\", name, \"order_status\", \"Served\")\n\n\n# Function to mark it as verified by a user in cancel type KOT\n@frappe.whitelist()\ndef confirm_cancel_kot(name, user):\n    frappe.db.set_value(\"URY KOT\", name, \"verified\", 1)\n    frappe.db.set_value(\"URY KOT\", name, \"verified_by\", user)\n\n\n@frappe.whitelist(allow_guest=True)\ndef get_site_name():\n    return {\"site_name\": frappe.local.site}\n\n@frappe.whitelist()\ndef kot_list():\n    today = frappe.utils.now()\n    branch = getBranch()\n    kot_alert_time = frappe.db.get_value(\n        \"POS Profile\", {\"branch\": branch}, \"custom_kot_warning_time\"\n    )\n    daily_order_number = frappe.db.get_value(\n        \"POS Profile\", {\"branch\": branch}, \"custom_reset_order_number_daily\"\n    )\n    three_hours_ago = frappe.utils.add_to_date(today, hours=-3)\n    audio_alert = frappe.db.get_value(\n        \"POS Profile\", {\"branch\": branch}, \"custom_kot_alert\"\n    )\n    kotList = frappe.get_list(\n        \"URY KOT\",\n        fields=[\"name\"],\n        filters={\n            \"order_status\": \"Ready For Prepare\",\n            \"branch\": branch,\n            \"type\": [\n                \"in\",\n                [\n                    \"New Order\",\n                    \"Order Modified\",\n                    \"Duplicate\",\n                    \"Cancelled\",\n                    \"Partially cancelled\",\n                ],\n            ],\n            \"docstatus\": 1,\n            \"verified\": 0,\n            \"creation\": (\">=\", three_hours_ago),\n        },\n        order_by=\"creation desc\",\n    )\n    KOT = []\n    for kot in kotList:\n        kotdoc = frappe.get_doc(\"URY KOT\", kot.name)\n        kotjson = json.loads(frappe.as_json(kotdoc))\n        KOT.append(kotjson)\n    return {\n        \"KOT\": KOT,\n        \"Branch\": branch,\n        \"kot_alert_time\": kot_alert_time,\n        \"audio_alert\": audio_alert,\n        \"daily_order_number\":daily_order_number\n    }\n\n@frappe.whitelist()\ndef served_kot_list():\n    today = frappe.utils.now()\n    branch = getBranch()\n    kot_alert_time = frappe.db.get_value(\n        \"POS Profile\", {\"branch\": branch}, \"custom_kot_warning_time\"\n    )\n    daily_order_number = frappe.db.get_value(\n        \"POS Profile\", {\"branch\": branch}, \"custom_reset_order_number_daily\"\n    )\n    three_hours_ago = frappe.utils.add_to_date(today, hours=-3)\n    audio_alert = frappe.db.get_value(\n        \"POS Profile\", {\"branch\": branch}, \"custom_kot_alert\"\n    )\n    kotList = frappe.get_list(\n        \"URY KOT\",\n        fields=[\"name\"],\n        filters={\n            \"order_status\": \"Served\",\n            \"branch\": branch,\n            \"type\": [\n                \"in\",\n                [\n                    \"New Order\",\n                    \"Order Modified\",\n                    \"Duplicate\",\n                    \"Cancelled\",\n                    \"Partially cancelled\",\n                ],\n            ],\n            \"docstatus\": 1,\n            \"verified\": 0,\n            \"creation\": (\">=\", three_hours_ago),\n        },\n        order_by=\"creation desc\",\n    )\n    print(kotList,\"kotList..................\")\n    KOT = []\n    for kot in kotList:\n        kotdoc = frappe.get_doc(\"URY KOT\", kot.name)\n        print(kot.name,\".................kotdoc\")\n        invoice=frappe.db.get_value(\"URY KOT\",kot.name,\"invoice\")\n        print(invoice,\".....................invoice\")\n        kotjson = json.loads(frappe.as_json(kotdoc))\n        KOT.append(kotjson)\n    return {\n        \"KOT\": KOT,\n        \"Branch\": branch,\n        \"kot_alert_time\": kot_alert_time,\n        \"audio_alert\": audio_alert,\n        \"daily_order_number\":daily_order_number\n    }\n\n"
  },
  {
    "path": "ury/ury/api/ury_kot_generate.py",
    "content": "import json\n\nimport frappe\nfrom ury.ury_pos.api import getBranch\n\n\n# Load JSON data or return as is if it's already a Python dictionary\ndef load_json(data):\n    if isinstance(data, str):\n        return json.loads(data)\n    return data\n\n\n# Create a list of order items from a list of input items\ndef create_order_items(items):\n    order_items = []\n    for item in items:\n        order_item = {\n            \"item_code\": item.get(\"item\", item.get(\"item_code\")),\n            \"qty\": item[\"qty\"],\n            \"item_name\": item[\"item_name\"],\n            \"comments\": item.get(\"comment\", item.get(\"comments\", \"\")),\n        }\n        order_items.append(order_item)\n    return order_items\n\n\n# Create a KOT (Kitchen Order Ticket) document\ndef create_kot_doc(\n    invoice_id,\n    customer,\n    restaurant_table,\n    items,\n    kot_type,\n    comments,\n    pos_profile_id,\n    kot_naming_series,\n    production,\n):\n    pos_invoice = frappe.get_doc(\"POS Invoice\", invoice_id)\n    order_number = pos_invoice.custom_ury_order_number\n    is_aggregator = 0\n    if pos_invoice.order_type == \"Aggregators\":\n        is_aggregator = 1\n    kot_doc = frappe.get_doc(\n        {\n            \"doctype\": \"URY KOT\",\n            \"invoice\": invoice_id,\n            \"restaurant_table\": restaurant_table,\n            \"customer_name\": customer,\n            \"pos_profile\": pos_profile_id,\n            \"comments\": comments,\n            \"type\": kot_type,\n            \"naming_series\": kot_naming_series,\n            \"production\": production,\n            \"aggregator_id\":pos_invoice.custom_aggregator_id,\n            \"is_aggregator\":is_aggregator,\n            \"order_no\":order_number\n        }\n    )\n    branch = getBranch()\n    if restaurant_table:\n        room = frappe.db.get_value(\"URY Table\", restaurant_table, \"restaurant_room\")\n        restaurant = frappe.db.get_value(\"URY Table\", restaurant_table, \"restaurant\")\n        menu = frappe.db.get_value(\"Menu for Room\", {\"room\": room,\"parent\":restaurant}, \"menu\")\n        \n    else:\n        menu = frappe.db.get_value(\"URY Restaurant\", {\"branch\": branch}, \"active_menu\")\n\n    for item in items:\n        course = frappe.db.get_value(\"URY Menu Item\", {\"item\": item[\"item_code\"],\"parent\":menu}, \"course\")\n        kot_doc.append(\n            \"kot_items\",\n            {\n                \"item\": item[\"item_code\"],\n                \"item_name\": item[\"item_name\"],\n                \"quantity\": item[\"qty\"],\n                \"comments\": item[\"comments\"],\n                \"course\":course\n            },\n        )\n    kot_doc.insert()\n    kot_doc.submit()\n\n# Function to get all production item groups for a given branch\ndef get_all_production_item_groups(branch):\n    productions = frappe.db.get_all(\n        \"URY Production Unit\", filters={\"branch\": branch}, fields=[\"name\"]\n    )\n    if productions:\n        all_production_item_groups = set()\n        for production in productions:\n            productionItemGroupslist = frappe.get_all(\n                \"URY Production Item Groups\",\n                fields=[\"item_group\"],\n                filters={\n                    \"parent\": production.name,\n                    \"parenttype\": \"URY Production Unit\",\n                },\n                order_by=\"idx\",\n            )\n            productionItemGroups = [\n                item_group.item_group for item_group in productionItemGroupslist\n            ]\n            all_production_item_groups.update(productionItemGroups)\n        return all_production_item_groups\n\n\n# Process items to create KOT documents\ndef process_items_for_kot(\n    invoice_id,\n    customer,\n    restaurant_table,\n    items,\n    comments,\n    pos_profile_id,\n    kot_naming_series,\n    kot_type,\n):\n    kot_items = create_order_items(items)\n    pos_profile = frappe.get_doc(\"POS Profile\", pos_profile_id)\n    productions = frappe.db.get_all(\n        \"URY Production Unit\", filters={\"branch\": pos_profile.branch}, fields=[\"name\"]\n    )\n\n    if productions:\n        all_production_item_groups = get_all_production_item_groups(pos_profile.branch)\n        \n        # Iterate through each item and check if item group belongs to a production unit\n        for item in kot_items:\n            item_group = frappe.db.get_value(\"Item\", item[\"item_code\"], \"item_group\")\n            item_code = item[\"item_code\"]\n            if item_group not in all_production_item_groups:\n                frappe.msgprint(\n                    f\"Item group '{item_group}' for item '{item_code}' is not in any production.\"\n                )\n        for production in productions:\n            productionItemGroupslist = frappe.get_all(\n                \"URY Production Item Groups\",\n                fields=[\"item_group\"],\n                filters={\n                    \"parent\": production.name,\n                    \"parenttype\": \"URY Production Unit\",\n                },\n                order_by=\"idx\",\n            )\n            productionItemGroups = [\n                item_group.item_group for item_group in productionItemGroupslist\n            ]\n            production_items = [\n                item\n                for item in kot_items\n                if frappe.db.get_value(\"Item\", item[\"item_code\"], \"item_group\")\n                in productionItemGroups\n            ]\n\n            if production_items:\n                invoice_exist = frappe.db.exists(\n                    \"URY KOT\",\n                    {\n                        \"invoice\": invoice_id,\n                        \"docstatus\": 1,\n                        \"production\": production.name,\n                    },\n                )\n                if invoice_exist:\n                    kot_type = \"Order Modified\"\n\n                create_kot_doc(\n                    invoice_id,\n                    customer,\n                    restaurant_table,\n                    production_items,\n                    kot_type,\n                    comments,\n                    pos_profile_id,\n                    kot_naming_series,\n                    production.name,\n                )\n    else:\n        frappe.throw(\n            \"Create URY Production unit against POS Profile: %s \" % pos_profile.name\n        )\n\n\n# Process items to create a cancel KOT document\ndef process_items_for_cancel_kot(\n    invoice_id,\n    customer,\n    restaurant_table,\n    items,\n    comments,\n    pos_profile_id,\n    cancel_kot_naming_series,\n    kot_type,\n    invoiceItems,\n):\n\n    kot_items = create_order_items(items)\n    pos_profile = frappe.get_doc(\"POS Profile\", pos_profile_id)\n    productions = frappe.db.get_all(\n        \"URY Production Unit\", filters={\"branch\": pos_profile.branch}, fields=[\"name\"]\n    )\n\n    for production in productions:\n        productionDoc = frappe.get_doc(\"URY Production Unit\", production.name)\n        productionItemGroups = [\n            item_group.item_group for item_group in productionDoc.item_groups\n        ]\n        production_items = [\n            item\n            for item in kot_items\n            if frappe.get_doc(\"Item\", item[\"item_code\"]).item_group\n            in productionItemGroups\n        ]\n\n        if production_items:\n            create_cancel_kot_doc(\n                invoice_id,\n                restaurant_table,\n                production_items,\n                kot_type,\n                customer,\n                comments,\n                pos_profile_id,\n                cancel_kot_naming_series,\n                invoiceItems,\n                production.name,\n            )\n\n\n# Create a cancel KOT document\ndef create_cancel_kot_doc(\n    invoice_id,\n    restaurant_table,\n    cancel_items,\n    kot_type,\n    customer,\n    comments,\n    pos_profile_id,\n    cancel_kot_naming_series,\n    invoiceItems,\n    production,\n):\n    pos_invoice = frappe.get_doc(\"POS Invoice\", invoice_id)\n    order_number = pos_invoice.custom_ury_order_number  \n    is_aggregator = 0\n    if pos_invoice.order_type == \"Aggregators\":\n        is_aggregator = 1\n    kot_list = frappe.db.get_list(\n        \"URY KOT\",\n        filters={\n            \"invoice\": invoice_id,\n            \"type\": (\"in\", (\"New Order\", \"Order Modified\")),\n        },\n        fields=(\"name\"),\n    )\n\n    # Find original KOTs related to the cancel items\n    original_kots = []\n    for cancelItem in cancel_items:\n        for kot in kot_list:\n            kot_doc = frappe.get_doc(\"URY KOT\", kot.name)\n            kot_cancel_items = kot_doc.kot_items\n            itemCheckFlag = False\n            for kotItem in kot_cancel_items:\n                if cancelItem[\"item_code\"] == kotItem.item:\n                    itemCheckFlag = True\n            if itemCheckFlag:\n                original_kots.append(kot_doc.name)\n                break\n\n    # Remove duplicate KOT names and join them into a single string\n    set_kots = [*set(original_kots)]\n    set_kots = \",\".join(set_kots)\n    kot_cancel_doc = frappe.get_doc(\n        {\n            \"doctype\": \"URY KOT\",\n            \"naming_series\": cancel_kot_naming_series,\n            \"original_kot\": set_kots,\n            \"restaurant_table\": restaurant_table,\n            \"customer_name\": customer,\n            \"type\": kot_type,\n            \"invoice\": invoice_id,\n            \"pos_profile\": pos_profile_id,\n            \"comments\": comments,\n            \"production\": production,\n            \"is_aggregator\":is_aggregator,\n            \"order_no\":order_number\n        }\n    )\n\n    branch = getBranch()\n    if restaurant_table:\n        room = frappe.db.get_value(\"URY Table\", restaurant_table, \"restaurant_room\")\n        restaurant = frappe.db.get_value(\"URY Table\", restaurant_table, \"restaurant\")\n        menu = frappe.db.get_value(\"Menu for Room\", {\"room\": room,\"parent\":restaurant}, \"menu\")\n        \n    else:\n        menu = frappe.db.get_value(\"URY Restaurant\", {\"branch\": branch}, \"active_menu\")\n    for cancelItem in cancel_items:\n        course = frappe.db.get_value(\"URY Menu Item\", {\"item\": cancelItem[\"item_code\"],\"parent\":menu}, \"course\")\n        for item in invoiceItems:\n            if cancelItem[\"item_code\"] == item[\"item_code\"]:\n                kot_cancel_doc.append(\n                    \"kot_items\",\n                    {\n                        \"item\": cancelItem[\"item_code\"],\n                        \"item_name\": cancelItem[\"item_name\"],\n                        \"cancelled_qty\": abs(int(cancelItem[\"qty\"])),\n                        \"quantity\": item[\"qty\"],\n                        \"comments\": cancelItem[\"comments\"],\n                        \"course\":course\n                    },\n                )\n\n    kot_cancel_doc.insert()\n    kot_cancel_doc.submit()\n\n\n# Whitelisted function to handle KOT entry\n@frappe.whitelist()\ndef kot_execute(\n    invoice_id,\n    customer,\n    restaurant_table=None,\n    current_items=[],\n    previous_items=[],\n    comments=None,\n):\n    current_items = load_json(current_items)\n    previous_items = load_json(previous_items)\n    new_invoice_items_array = create_order_items(previous_items)\n    new_Order_items_array = create_order_items(current_items)\n\n    final_array = compare_two_array(new_Order_items_array, new_invoice_items_array)\n    removed_item = get_removed_items(new_invoice_items_array, new_Order_items_array)\n\n    pos_invoice = frappe.get_doc(\"POS Invoice\", invoice_id)\n    pos_profile_id = pos_invoice.pos_profile\n    pos_profile = frappe.get_doc(\"POS Profile\", pos_profile_id)\n    kot_naming_series = pos_profile.custom_kot_naming_series\n    if kot_naming_series:\n        cancel_kot_naming_series = \"CNCL-\" + kot_naming_series\n    else:\n        frappe.throw(\n            \"KOT Naming Series is mandatory for the auto creation of KOT.Ensure it is configured in the POS Profile: %s\"\n            % pos_profile.name\n        )\n\n    positive_qty_items = [item for item in final_array if int(item[\"qty\"]) > 0]\n    negative_qty_items = [item for item in final_array if int(item[\"qty\"]) <= 0]\n    total_cancel_items = negative_qty_items + removed_item\n    if positive_qty_items:\n        process_items_for_kot(\n            invoice_id,\n            customer,\n            restaurant_table,\n            positive_qty_items,\n            comments,\n            pos_profile_id,\n            kot_naming_series,\n            \"New Order\",\n        )\n    if total_cancel_items:\n        process_items_for_cancel_kot(\n            invoice_id,\n            customer,\n            restaurant_table,\n            total_cancel_items,\n            comments,\n            pos_profile_id,\n            cancel_kot_naming_series,\n            \"Partially cancelled\",\n            new_invoice_items_array,\n        )\n\n\n# Compare two arrays and return the items that are different\ndef compare_two_array(array_1, array_2):\n    finalarray = []\n    for index, x in enumerate(array_1):\n        a = list(\n            filter(\n                lambda y: y[\"item_code\"] == x[\"item_code\"] and y[\"qty\"] == x[\"qty\"],\n                array_2,\n            )\n        )\n        if len(a) == 0:\n            b = list(filter(lambda z: z[\"item_code\"] == x[\"item_code\"], array_2))\n            for qtb in b:\n                x[\"qty\"] = int(x[\"qty\"]) - int(qtb[\"qty\"])\n            finalarray.append(x)\n    return finalarray\n\n\n# Get the items that have been removed from the second array compared to the first array\ndef get_removed_items(array_1, array_2):\n    removed_objects = [\n        obj\n        for obj in array_1\n        if obj[\"item_code\"] not in [x[\"item_code\"] for x in array_2]\n    ]\n    return removed_objects\n"
  },
  {
    "path": "ury/ury/api/ury_kot_notification.py",
    "content": "import frappe\n\n\ndef get_users_with_role(role_name):\n    users_with_role = frappe.get_all(\n        \"Has Role\", filters={\"role\": role_name}, fields=[\"parent as user\"]\n    )\n\n    user_ids = [user[\"user\"] for user in users_with_role]\n    user_details = frappe.get_all(\n        \"User\",\n        filters={\"name\": (\"in\", user_ids)},\n        fields=[\"name\", \"full_name\", \"email\"],\n    )\n\n    return user_details\n\n\n@frappe.whitelist()\ndef order_delay_notification(id):\n    table = frappe.db.get_value(\"URY KOT\", id, \"restaurant_table\")\n    tableOrTakeaway = \"Take Away\"\n    if table:\n        tableOrTakeaway = table\n    order_status = frappe.db.get_value(\"URY KOT\", id, \"order_status\")\n    invoice_id = frappe.db.get_value(\"URY KOT\", id, \"invoice\")\n    order_id = invoice_id[-5:]\n    kot_type = frappe.db.get_value(\"URY KOT\", id, \"type\")\n    pos_profile = frappe.db.get_value(\"URY KOT\", id, \"pos_profile\")\n    items = frappe.get_all(\n        \"URY KOT Items\",\n        fields=[\"item_name\", \"quantity\"],\n        filters={\"parent\": id, \"parenttype\": \"KOT\"},\n        order_by=\"idx\",\n    )\n\n    subject = f\"\"\"Order # {order_id} Delayed\"\"\"\n\n    message = f\"\"\"\n            <ul>\n                <li><b> Table : </b> {tableOrTakeaway}</li>\n                <li><b> Order Type : </b> {kot_type}</li>\n                \n            \n            <ul>\n    \"\"\"\n\n    message += \"\"\"\n            </ul></li>\n            </ul>\n    \"\"\"\n\n    receipients = frappe.get_all(\n        \"URY Notification Recipient\",\n        fields=[\"receiver_by_role\"],\n        filters={\"parent\": pos_profile, \"parenttype\": \"POS Profile\"},\n        order_by=\"idx\",\n    )\n    if order_status == \"Ready For Prepare\":\n        for receipient in receipients:\n            users = get_users_with_role(receipient.receiver_by_role)\n            for user in users:\n                create_system_notification(message, user.name, subject)\n\n\ndef create_system_notification(message, user, subject):\n    communication = frappe.get_doc(\n        {\n            \"doctype\": \"Notification Log\",\n            \"email_content\": message,\n            \"for_user\": user,\n            \"subject\": subject,\n            \"type\": \"Alert\",\n        }\n    )\n\n    communication.insert(ignore_permissions=True)\n"
  },
  {
    "path": "ury/ury/api/ury_kot_order_number.py",
    "content": "import frappe\n\n\ndef set_order_number(doc, event):\n    pos_profile = doc.pos_profile\n    if doc.order_type == \"Aggregators\":\n        last_invoice = frappe.get_value(\n            \"POS Opening Entry\",\n            {\"pos_profile\": pos_profile, \"status\": \"Open\"},\n            \"custom_ury_last_aggregator_invoice\",\n        )\n    else:\n        last_invoice = frappe.get_value(\n            \"POS Opening Entry\",\n            {\"pos_profile\": pos_profile, \"status\": \"Open\"},\n            \"custom_ury_last_invoice\",\n        )\n    if last_invoice:\n        last_invoice_number = int(last_invoice[-5:])\n\n        current_invoice = doc.name\n\n        current_invoice_number = int(current_invoice[-5:])\n\n        order_number = current_invoice_number - last_invoice_number\n        if order_number > 0:\n            if doc.order_type == \"Aggregators\":\n                order_number = \"AGR - \" + str(order_number)\n            frappe.db.set_value(\n                \"POS Invoice\",\n                doc.name,\n                \"custom_ury_order_number\",\n                order_number,\n                update_modified=False,\n            )\n        else:\n            frappe.db.set_value(\n                \"POS Invoice\",\n                doc.name,\n                \"custom_ury_order_number\",\n                current_invoice_number,\n                update_modified=False,\n            )\n    else:\n        pos_open_name = frappe.get_value(\n            \"POS Opening Entry\",\n            {\"pos_profile\": pos_profile, \"status\": \"Open\"},\n            \"name\",\n        )\n\n        # invoice = frappe.get_last_doc(\n        #     \"POS Invoice\", filters={\"pos_profile\": doc.pos_profile}\n        # )\n\n        if doc.order_type == \"Aggregators\":\n            aggregator_invoice =frappe.get_last_doc(\n                \"POS Invoice\", filters={\"pos_profile\": doc.pos_profile,\"order_type\": \"Aggregators\"}\n            )\n            aggregator_invoice_number = int(aggregator_invoice.name[-5:])\n            aggregator_last_order_number = aggregator_invoice_number - 1\n            frappe.db.set_value(\n                \"POS Opening Entry\", pos_open_name, \"custom_ury_last_aggregator_invoice\", aggregator_last_order_number\n            )\n        else:\n            invoice = frappe.get_last_doc(\n                \"POS Invoice\", filters={\"pos_profile\": doc.pos_profile,\"order_type\": [\"!=\", \"Aggregators\"]}\n            )\n\n            invoice_number = int(invoice.name[-5:])\n            last_order_number = invoice_number - 1\n\n            frappe.db.set_value(\n                \"POS Opening Entry\", pos_open_name, \"custom_ury_last_invoice\", last_order_number\n            )\n        \n        default_value = \"AGR - 1\" if doc.order_type == \"Aggregators\" else \"1\"\n        frappe.db.set_value(\n            \"POS Invoice\",\n            doc.name,\n            \"custom_ury_order_number\",\n            default_value,\n            update_modified=False,  \n        )\n\n\ndef set_last_invoice_in_pos_open(doc, event):\n    try:\n        invoice = frappe.get_last_doc(\n            \"POS Invoice\", filters={\"pos_profile\": doc.pos_profile,\"order_type\": [\"!=\", \"Aggregators\"]}\n        )\n        doc.custom_ury_last_invoice = invoice.name\n    except:\n        pass \n    try:\n        aggregator_invoice =frappe.get_last_doc(\n            \"POS Invoice\", filters={\"pos_profile\": doc.pos_profile,\"order_type\": \"Aggregators\"}\n        )\n        doc.custom_ury_last_aggregator_invoice = aggregator_invoice.name\n    except:\n        pass"
  },
  {
    "path": "ury/ury/api/ury_kot_reprint.py",
    "content": "import frappe\nfrom frappe.utils import cint\nfrom frappe.utils.print_format import print_by_server\n\n\n\n@frappe.whitelist()\ndef reprint_kot(invoice_number):\n\n    try:\n        pos_profile, restaurant_table, order_type = frappe.db.get_value(\n            \"POS Invoice\", invoice_number, [\"pos_profile\", \"restaurant_table\",\"order_type\"]\n        )\n        if not pos_profile:\n            frappe.throw(f\"POS Profile not found for Invoice {invoice_number}.\")\n\n        enable_kot_reprint, kot_print_format, table_order_printer, parcel_order_printer = frappe.db.get_value(\n            \"POS Profile\", pos_profile,\n            [\"custom_enable_kot_reprint\", \"custom_reprint_kot_format\", \"custom_table_order_printer\", \"custom_parcel_order_printer\"]\n        )\n\n        \n        if not cint(enable_kot_reprint):\n            frappe.throw(\"KOT Reprint is disabled in POS Profile.\")\n\n        if not kot_print_format:\n            frappe.throw(\"No KOT Reprint Print Format is set in POS Profile.\")\n        \n        printer = table_order_printer if order_type == \"Dine In\" else parcel_order_printer\n\n        if not printer:\n            frappe.throw(\"No printer is assigned for reprinting KOT.\")\n\n       \n        print_kot(printer, invoice_number, kot_print_format)\n\n\n        return \"Success\"\n\n    except Exception as e:\n        error_message = f\"KOT Reprint Error for Invoice {invoice_number}: {str(e)}\"\n        frappe.log_error(error_message, \"KOT Reprint Error\")\n        frappe.throw(\"An unexpected error occurred while reprinting KOT. Please check logs.\")\n\n\ndef print_kot(printer,docname, kot_print_format):\n    try:\n        print_by_server(\"POS Invoice\",docname, printer, kot_print_format)\n    except Exception as e:\n        frappe.log_error(f\"KOT Reprint Error: {e}\")"
  },
  {
    "path": "ury/ury/api/ury_kot_validation.py",
    "content": "import frappe\nimport json\n\nfrom frappe.utils import get_datetime, datetime\n\n\ndef kotValidationThread():\n    current_datetime = get_datetime()\n    one_minute_ago = current_datetime - datetime.timedelta(minutes=1)\n    five_minutes_ago = current_datetime - datetime.timedelta(minutes=5)\n\n    # Get a list of unprocessed invoices within the last 5 minutes\n    invoice_list = get_unprocessed_invoices(five_minutes_ago, one_minute_ago)\n\n    # Process each invoice\n    for invoice in invoice_list:\n        process_invoice(invoice)\n\n\n# Function to fetch unprocessed invoices within a time range\ndef get_unprocessed_invoices(start_time, end_time):\n    return frappe.db.sql(\n        \"\"\"\n        SELECT name, creation\n        FROM `tabPOS Invoice`\n        WHERE docstatus = 0\n            AND creation BETWEEN %s AND %s\n        \"\"\",\n        (start_time, end_time),\n        as_dict=True,\n    )\n\n\n# Function to process an invoice\ndef process_invoice(invoice):\n    posInvoice = frappe.get_doc(\"POS Invoice\", invoice)\n    waiter = posInvoice.waiter\n    pos_profile = frappe.get_doc(\"POS Profile\", posInvoice.pos_profile)\n\n    # Determine the owner based on the restaurant table\n    owner = waiter if not invoice.restaurant_table else waiter\n\n    kot_naming_series = pos_profile.kot_naming_series\n    kot_list = frappe.get_list(\n        \"URY KOT\",\n        filters={\"creation\": (\">\", posInvoice.creation), \"invoice\": posInvoice.name},\n    )\n\n    # If no KOT exists for the invoice, process it\n    if not kot_list:\n        production_items = []\n        productionDoc = None\n\n        # Fetch production units for the branch\n        productions = get_productions_for_branch(posInvoice.branch)\n\n        for p in productions:\n            productionDoc = frappe.get_doc(\"URY Production Unit\", p.name)\n            productionItemGroups = [\n                item_group.item_group for item_group in productionDoc.item_groups\n            ]\n            p_flag = 0\n\n            # Check if items in the invoice belong to production groups\n            for i in posInvoice.items:\n                item = frappe.get_doc(\"Item\", i.item_code)\n                if item.item_group in productionItemGroups:\n                    p_flag = 1\n                    production_items.append(i)\n\n            if p_flag == 1:\n                create_kot(\n                    invoice,\n                    pos_profile,\n                    kot_naming_series,\n                    production_items,\n                    owner,\n                    p.name,\n                )\n\n\n# Function to fetch production units for a branch\ndef get_productions_for_branch(branch):\n    return frappe.get_all(\n        \"URY Production Unit\", filters={\"branch\": branch}, fields=[\"name\", \"item_groups\"]\n    )\n\n\n# Function to create a KOT\ndef create_kot(\n    invoice, pos_profile, kot_naming_series, production_items, owner, production_name\n):\n    posInvoice = frappe.get_doc(\"POS Invoice\", invoice)\n    kotdoc = frappe.new_doc(\"URY KOT\")\n    kotdoc.update(\n        {\n            \"invoice\": posInvoice.name,\n            \"restaurant_table\": posInvoice.restaurant_table,\n            \"naming_series\": kot_naming_series,\n            \"type\": \"Duplicate\",\n            \"pos_profile\": pos_profile.name,\n            \"customer_name\": posInvoice.customer,\n            \"production\": production_name,\n            \"order_no\": posInvoice.order_no\n            if hasattr(posInvoice, \"order_no\")\n            else None,\n        }\n    )\n\n    for pr in production_items:\n        kotdoc.append(\n            \"kot_items\",\n            {\"item\": pr.item_code, \"item_name\": pr.item_name, \"quantity\": pr.qty},\n        )\n\n    kotdoc.insert()\n    kotdoc.submit()\n    kotdoc.db_set(\"owner\", owner)\n\n    # Create a KOT Log entry\n    create_kot_log(kotdoc, invoice)\n\n\n# Function to create a KOT Log entry\ndef create_kot_log(kotdoc, invoice):\n    posInvoice = frappe.get_doc(\"POS Invoice\", invoice)\n    KOTLog = frappe.new_doc(\"URY KOT Error Log\")\n    KOTLog.update(\n        {\n            \"kot\": kotdoc.name,\n            \"invoice\": posInvoice.name,\n            \"invoice_creation_time\": posInvoice.creation,\n        }\n    )\n\n    KOTLog.insert()\n"
  },
  {
    "path": "ury/ury/api/ury_menu_course_validation.py",
    "content": "import frappe\n\ndef validate_priority(doc,event):\n    # Check if the selected priority is already used by another course\n    existing_course = frappe.db.exists(\n        'URY Menu Course', \n        {\n            'custom_serving_priority': doc.custom_serving_priority,\n            'name': ['!=', doc.name]  # Exclude the current record\n        }\n    )\n\n    if existing_course:\n        frappe.throw(f\"Priority {doc.custom_serving_priority} is already assigned to another course. Please choose a different priority.\")\n"
  },
  {
    "path": "ury/ury/api/ury_print.py",
    "content": "import frappe\nfrom frappe import _\n\nimport os\n\nfrom pypdf import PdfWriter\n\nno_cache = 1\n\nbase_template_path = \"www/printview.html\"\nstandard_format = \"templates/print_formats/standard.html\"\n\nfrom frappe.www.printview import validate_print_permission\n\n\n@frappe.whitelist()\ndef network_printing(\n    doctype,\n    name,\n    printer_setting,\n    print_format=None,\n    doc=None,\n    no_letterhead=0,\n    file_path=None,\n):\n    try:\n        print_settings = frappe.get_doc(\"Network Printer Settings\", printer_setting)\n\n        try:\n            import cups\n        except ImportError:\n            return \"Failed to import cups\"\n\n        try:\n            cups.setServer(print_settings.server_ip)\n            cups.setPort(print_settings.port)\n            conn = cups.Connection()\n        except Exception as e:\n            return f\"Failed to connect to the printer: {str(e)}\"\n\n        try:\n            output = PdfWriter()\n            output = frappe.get_print(\n                doctype,\n                name,\n                print_format,\n                doc=doc,\n                no_letterhead=no_letterhead,\n                as_pdf=True,\n                output=output,\n            )\n            if not file_path:\n                file_path = os.path.join(\n                    \"/\", \"tmp\", f\"frappe-pdf-{frappe.generate_hash()}.pdf\"\n                )\n            with open(file_path, \"wb\") as f:\n                output.write(f)\n            conn.printFile(print_settings.printer_name, file_path, name, {})\n\n            restaurant_table, invoice_printed, name = frappe.db.get_value(\n                \"POS Invoice\", name, [\"restaurant_table\", \"invoice_printed\", \"name\"]\n            )\n\n            if restaurant_table and invoice_printed == 0:\n                frappe.db.set_value(\"POS Invoice\", name, \"invoice_printed\", 1)\n                frappe.db.set_value(\n                    \"URY Table\",\n                    restaurant_table,\n                    {\"occupied\": 0, \"latest_invoice_time\": None},\n                )\n            else:\n                frappe.db.set_value(\"POS Invoice\", name, \"invoice_printed\", 1)\n\n            return \"Success\"\n        except Exception as e:\n            return f\"Failed to print: {str(e)}\"\n    except Exception as e:\n        import traceback\n\n        traceback.print_exc()  # Print the full traceback for debugging\n        return f\"An error occurred: {str(e)}\"\n\n\n@frappe.whitelist()\ndef select_network_printer(pos_profile, invoice_id):\n    table = frappe.db.get_value(\"POS Invoice\", invoice_id, \"restaurant_table\")\n    print_format = frappe.db.get_value(\"POS Profile\", pos_profile, \"print_format\")\n\n    if table:\n        room = frappe.db.get_value(\"URY Table\", table, \"restaurant_room\")\n        room_bill_printer = frappe.db.get_value(\n            \"URY Printer Settings\", {\"parent\": room, \"bill\": 1}, \"printer\"\n        )\n        if room_bill_printer:\n            print = network_printing(\n                \"POS Invoice\", invoice_id, room_bill_printer, print_format\n            )\n            return print\n\n    else:\n        pos_bill_printer = frappe.db.get_value(\n            \"URY Printer Settings\", {\"parent\": pos_profile, \"bill\": 1}, \"printer\"\n        )\n        if pos_bill_printer:\n            print = network_printing(\n                \"POS Invoice\", invoice_id, pos_bill_printer, print_format\n            )\n            return print\n\n\n@frappe.whitelist()\ndef qz_print_update(invoice):\n    try:\n        table = frappe.db.get_value(\"POS Invoice\", invoice, \"restaurant_table\")\n        \n        if table == None or table == \"\":\n            # Update invoice_printed\n            frappe.db.set_value(\n                \"POS Invoice\", invoice, \"invoice_printed\", 1, update_modified=False\n            )\n            \n            # Validate the update\n            new_invoice_printed = frappe.db.get_value(\"POS Invoice\", invoice, \"invoice_printed\")\n            if new_invoice_printed != 1:\n                return {\"status\": \"Failure\"}                \n        else:\n            invoice_printed = frappe.db.get_value(\"POS Invoice\", invoice, \"invoice_printed\")\n\n            if invoice_printed == 0:\n                # Update invoice_printed\n                frappe.db.set_value(\n                    \"POS Invoice\", invoice, \"invoice_printed\", 1, update_modified=False\n                )\n                \n                # Update table status\n                frappe.db.set_value(\n                    \"URY Table\", table, {\"occupied\": 0, \"latest_invoice_time\": None}\n                )\n                \n                # Validate both updates\n                new_invoice_printed = frappe.db.get_value(\"POS Invoice\", invoice, \"invoice_printed\")\n                new_table_status = frappe.db.get_value(\"URY Table\", table, \"occupied\")\n                \n                if new_invoice_printed != 1 or new_table_status != 0:\n                    return {\"status\": \"Failure\"}\n        \n        return {\"status\": \"Success\"}\n        \n    except Exception as e:\n        frappe.log_error(message=e, title=\"Print Fail\")\n        frappe.throw(_(\"Error while printing order\",e))                   \n        return {\"status\": \"Failure\"}\n\n\n@frappe.whitelist()\ndef print_pos_page(doctype, name, print_format):\n    data = {\"name\": name, \"doctype\": doctype, \"print_format\": print_format}\n\n    restaurant_table, branch, name = frappe.db.get_value(\n        \"POS Invoice\", name, [\"restaurant_table\", \"branch\", \"name\"]\n    )\n    print_channel = \"{}_{}\".format(\"print\", branch)\n    frappe.publish_realtime(print_channel, {\"data\": data})\n\n    invoice_printed = frappe.db.get_value(\"POS Invoice\", name, \"invoice_printed\")\n\n    if invoice_printed == 0:\n        frappe.db.set_value(\"POS Invoice\", name, \"invoice_printed\", 1)\n\n        if restaurant_table:\n            frappe.db.set_value(\n                \"URY Table\",\n                restaurant_table,\n                {\"occupied\": 0, \"latest_invoice_time\": None},\n            )\n\n\n@frappe.whitelist()\ndef qz_certificate():\n    site_config = frappe.get_site_config()\n    qz_key_value = site_config.get(\"qz_cert\")\n\n    return qz_key_value\n\n\n@frappe.whitelist()\ndef signature_promise():\n    site_config = frappe.get_site_config()\n    key_value = site_config.get(\"qz_private_key\")\n\n    return key_value\n"
  },
  {
    "path": "ury/ury/custom/item.json",
    "content": "{\n \"custom_fields\": [\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"allow_in_quick_entry\": 0,\n   \"allow_on_submit\": 0,\n   \"bold\": 0,\n   \"collapsible\": 0,\n   \"collapsible_depends_on\": null,\n   \"columns\": 0,\n   \"creation\": \"2025-07-25 18:49:16.938354\",\n   \"default\": null,\n   \"depends_on\": null,\n   \"description\": \"Please add this item to the URY Menu to make it available in POS\",\n   \"docstatus\": 0,\n   \"dt\": \"Item\",\n   \"fetch_from\": null,\n   \"fetch_if_empty\": 0,\n   \"fieldname\": \"custom_pos_add_on_items\",\n   \"fieldtype\": \"Table\",\n   \"hidden\": 0,\n   \"hide_border\": 0,\n   \"hide_days\": 0,\n   \"hide_seconds\": 0,\n   \"idx\": 55,\n   \"ignore_user_permissions\": 0,\n   \"ignore_xss_filter\": 0,\n   \"in_global_search\": 0,\n   \"in_list_view\": 0,\n   \"in_preview\": 0,\n   \"in_standard_filter\": 0,\n   \"insert_after\": \"custom_pos_variants\",\n   \"is_system_generated\": 0,\n   \"is_virtual\": 0,\n   \"label\": \"POS Add On Items\",\n   \"length\": 0,\n   \"link_filters\": null,\n   \"mandatory_depends_on\": null,\n   \"modified\": \"2025-07-25 18:49:16.938354\",\n   \"modified_by\": \"Administrator\",\n   \"module\": \"URY\",\n   \"name\": \"Item-custom_pos_add_on_items\",\n   \"no_copy\": 0,\n   \"non_negative\": 0,\n   \"options\": \"Item Add On\",\n   \"owner\": \"Administrator\",\n   \"permlevel\": 0,\n   \"placeholder\": null,\n   \"precision\": \"\",\n   \"print_hide\": 0,\n   \"print_hide_if_no_value\": 0,\n   \"print_width\": null,\n   \"read_only\": 0,\n   \"read_only_depends_on\": null,\n   \"report_hide\": 0,\n   \"reqd\": 0,\n   \"search_index\": 0,\n   \"show_dashboard\": 0,\n   \"sort_options\": 0,\n   \"translatable\": 0,\n   \"unique\": 0,\n   \"width\": null\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"allow_in_quick_entry\": 0,\n   \"allow_on_submit\": 0,\n   \"bold\": 0,\n   \"collapsible\": 0,\n   \"collapsible_depends_on\": null,\n   \"columns\": 0,\n   \"creation\": \"2025-07-25 18:55:01.853644\",\n   \"default\": null,\n   \"depends_on\": null,\n   \"description\": \"Please add this item to the URY Menu to make it available in POS\",\n   \"docstatus\": 0,\n   \"dt\": \"Item\",\n   \"fetch_from\": null,\n   \"fetch_if_empty\": 0,\n   \"fieldname\": \"custom_pos_item_variants\",\n   \"fieldtype\": \"Table\",\n   \"hidden\": 0,\n   \"hide_border\": 0,\n   \"hide_days\": 0,\n   \"hide_seconds\": 0,\n   \"idx\": 56,\n   \"ignore_user_permissions\": 0,\n   \"ignore_xss_filter\": 0,\n   \"in_global_search\": 0,\n   \"in_list_view\": 0,\n   \"in_preview\": 0,\n   \"in_standard_filter\": 0,\n   \"insert_after\": \"custom_pos_add_on_items\",\n   \"is_system_generated\": 0,\n   \"is_virtual\": 0,\n   \"label\": \"POS Item Variants\",\n   \"length\": 0,\n   \"link_filters\": null,\n   \"mandatory_depends_on\": null,\n   \"modified\": \"2025-07-25 18:55:01.853644\",\n   \"modified_by\": \"Administrator\",\n   \"module\": \"URY\",\n   \"name\": \"Item-custom_pos_item_variants\",\n   \"no_copy\": 0,\n   \"non_negative\": 0,\n   \"options\": \"POS Item Variants\",\n   \"owner\": \"Administrator\",\n   \"permlevel\": 0,\n   \"placeholder\": null,\n   \"precision\": \"\",\n   \"print_hide\": 0,\n   \"print_hide_if_no_value\": 0,\n   \"print_width\": null,\n   \"read_only\": 0,\n   \"read_only_depends_on\": null,\n   \"report_hide\": 0,\n   \"reqd\": 0,\n   \"search_index\": 0,\n   \"show_dashboard\": 0,\n   \"sort_options\": 0,\n   \"translatable\": 0,\n   \"unique\": 0,\n   \"width\": null\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"allow_in_quick_entry\": 0,\n   \"allow_on_submit\": 0,\n   \"bold\": 0,\n   \"collapsible\": 0,\n   \"collapsible_depends_on\": null,\n   \"columns\": 0,\n   \"creation\": \"2025-07-25 18:47:17.231893\",\n   \"default\": null,\n   \"depends_on\": null,\n   \"description\": null,\n   \"docstatus\": 0,\n   \"dt\": \"Item\",\n   \"fetch_from\": null,\n   \"fetch_if_empty\": 0,\n   \"fieldname\": \"custom_pos_variants\",\n   \"fieldtype\": \"Tab Break\",\n   \"hidden\": 0,\n   \"hide_border\": 0,\n   \"hide_days\": 0,\n   \"hide_seconds\": 0,\n   \"idx\": 54,\n   \"ignore_user_permissions\": 0,\n   \"ignore_xss_filter\": 0,\n   \"in_global_search\": 0,\n   \"in_list_view\": 0,\n   \"in_preview\": 0,\n   \"in_standard_filter\": 0,\n   \"insert_after\": \"serial_no_series\",\n   \"is_system_generated\": 0,\n   \"is_virtual\": 0,\n   \"label\": \"POS Variants\",\n   \"length\": 0,\n   \"link_filters\": null,\n   \"mandatory_depends_on\": null,\n   \"modified\": \"2025-07-25 18:47:17.231893\",\n   \"modified_by\": \"Administrator\",\n   \"module\": \"URY\",\n   \"name\": \"Item-custom_pos_variants\",\n   \"no_copy\": 0,\n   \"non_negative\": 0,\n   \"options\": null,\n   \"owner\": \"Administrator\",\n   \"permlevel\": 0,\n   \"placeholder\": null,\n   \"precision\": \"\",\n   \"print_hide\": 0,\n   \"print_hide_if_no_value\": 0,\n   \"print_width\": null,\n   \"read_only\": 0,\n   \"read_only_depends_on\": null,\n   \"report_hide\": 0,\n   \"reqd\": 0,\n   \"search_index\": 0,\n   \"show_dashboard\": 0,\n   \"sort_options\": 0,\n   \"translatable\": 0,\n   \"unique\": 0,\n   \"width\": null\n  }\n ],\n \"custom_perms\": [],\n \"doctype\": \"Item\",\n \"links\": [],\n \"property_setters\": [\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2025-07-03 18:00:04.823388\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"barcodes\",\n   \"idx\": 0,\n   \"is_system_generated\": 1,\n   \"modified\": \"2025-07-03 18:00:04.823388\",\n   \"modified_by\": \"sanjusha@tridz.com\",\n   \"module\": \"URY\",\n   \"name\": \"Item-barcodes-hidden\",\n   \"owner\": \"sanjusha@tridz.com\",\n   \"property\": \"hidden\",\n   \"property_type\": \"Check\",\n   \"row_name\": null,\n   \"value\": \"0\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2025-07-03 18:00:04.287232\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"item_code\",\n   \"idx\": 0,\n   \"is_system_generated\": 1,\n   \"modified\": \"2025-07-03 18:00:04.287232\",\n   \"modified_by\": \"sanjusha@tridz.com\",\n   \"module\": \"URY\",\n   \"name\": \"Item-item_code-hidden\",\n   \"owner\": \"sanjusha@tridz.com\",\n   \"property\": \"hidden\",\n   \"property_type\": \"Check\",\n   \"row_name\": null,\n   \"value\": \"0\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2025-07-03 18:00:04.299884\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"item_code\",\n   \"idx\": 0,\n   \"is_system_generated\": 1,\n   \"modified\": \"2025-07-03 18:00:04.299884\",\n   \"modified_by\": \"sanjusha@tridz.com\",\n   \"module\": \"URY\",\n   \"name\": \"Item-item_code-reqd\",\n   \"owner\": \"sanjusha@tridz.com\",\n   \"property\": \"reqd\",\n   \"property_type\": \"Check\",\n   \"row_name\": null,\n   \"value\": \"1\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2026-03-30 23:00:07.558098\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"item_name\",\n   \"idx\": 0,\n   \"is_system_generated\": 0,\n   \"modified\": \"2026-03-30 23:00:07.558098\",\n   \"modified_by\": \"Administrator\",\n   \"module\": null,\n   \"name\": \"Item-item_name-translatable\",\n   \"owner\": \"Administrator\",\n   \"property\": \"translatable\",\n   \"property_type\": \"Check\",\n   \"row_name\": null,\n   \"value\": \"1\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2026-03-30 23:00:07.380227\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocType\",\n   \"field_name\": null,\n   \"idx\": 0,\n   \"is_system_generated\": 0,\n   \"modified\": \"2026-03-30 23:00:07.380227\",\n   \"modified_by\": \"Administrator\",\n   \"module\": \"URY\",\n   \"name\": \"Item-main-field_order\",\n   \"owner\": \"Administrator\",\n   \"property\": \"field_order\",\n   \"property_type\": \"Data\",\n   \"row_name\": null,\n   \"value\": \"[\\\"details\\\", \\\"naming_series\\\", \\\"item_code\\\", \\\"item_name\\\", \\\"item_group\\\", \\\"stock_uom\\\", \\\"column_break0\\\", \\\"disabled\\\", \\\"allow_alternative_item\\\", \\\"is_stock_item\\\", \\\"has_variants\\\", \\\"opening_stock\\\", \\\"valuation_rate\\\", \\\"standard_rate\\\", \\\"is_fixed_asset\\\", \\\"auto_create_assets\\\", \\\"is_grouped_asset\\\", \\\"asset_category\\\", \\\"asset_naming_series\\\", \\\"over_delivery_receipt_allowance\\\", \\\"over_billing_allowance\\\", \\\"image\\\", \\\"section_break_11\\\", \\\"description\\\", \\\"brand\\\", \\\"unit_of_measure_conversion\\\", \\\"uoms\\\", \\\"dashboard_tab\\\", \\\"inventory_section\\\", \\\"inventory_settings_section\\\", \\\"shelf_life_in_days\\\", \\\"end_of_life\\\", \\\"default_material_request_type\\\", \\\"valuation_method\\\", \\\"column_break1\\\", \\\"warranty_period\\\", \\\"weight_per_unit\\\", \\\"weight_uom\\\", \\\"allow_negative_stock\\\", \\\"sb_barcodes\\\", \\\"barcodes\\\", \\\"reorder_section\\\", \\\"reorder_levels\\\", \\\"serial_nos_and_batches\\\", \\\"has_batch_no\\\", \\\"create_new_batch\\\", \\\"batch_number_series\\\", \\\"has_expiry_date\\\", \\\"retain_sample\\\", \\\"sample_quantity\\\", \\\"column_break_37\\\", \\\"has_serial_no\\\", \\\"serial_no_series\\\", \\\"custom_pos_variants\\\", \\\"custom_pos_add_on_items\\\", \\\"custom_pos_item_variants\\\", \\\"variants_section\\\", \\\"variant_of\\\", \\\"variant_based_on\\\", \\\"attributes\\\", \\\"accounting\\\", \\\"deferred_accounting_section\\\", \\\"enable_deferred_expense\\\", \\\"no_of_months_exp\\\", \\\"column_break_9s9o\\\", \\\"enable_deferred_revenue\\\", \\\"no_of_months\\\", \\\"section_break_avcp\\\", \\\"item_defaults\\\", \\\"purchasing_tab\\\", \\\"purchase_uom\\\", \\\"min_order_qty\\\", \\\"safety_stock\\\", \\\"is_purchase_item\\\", \\\"purchase_details_cb\\\", \\\"lead_time_days\\\", \\\"last_purchase_rate\\\", \\\"is_customer_provided_item\\\", \\\"customer\\\", \\\"supplier_details\\\", \\\"delivered_by_supplier\\\", \\\"column_break2\\\", \\\"supplier_items\\\", \\\"foreign_trade_details\\\", \\\"country_of_origin\\\", \\\"column_break_59\\\", \\\"customs_tariff_number\\\", \\\"sales_details\\\", \\\"sales_uom\\\", \\\"grant_commission\\\", \\\"is_sales_item\\\", \\\"column_break3\\\", \\\"max_discount\\\", \\\"customer_details\\\", \\\"customer_items\\\", \\\"item_tax_section_break\\\", \\\"taxes\\\", \\\"quality_tab\\\", \\\"inspection_required_before_purchase\\\", \\\"quality_inspection_template\\\", \\\"inspection_required_before_delivery\\\", \\\"manufacturing\\\", \\\"include_item_in_manufacturing\\\", \\\"is_sub_contracted_item\\\", \\\"default_bom\\\", \\\"column_break_74\\\", \\\"customer_code\\\", \\\"default_item_manufacturer\\\", \\\"default_manufacturer_part_no\\\", \\\"total_projected_qty\\\"]\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2025-07-03 18:00:04.275625\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"naming_series\",\n   \"idx\": 0,\n   \"is_system_generated\": 1,\n   \"modified\": \"2025-07-03 18:00:04.275625\",\n   \"modified_by\": \"sanjusha@tridz.com\",\n   \"module\": \"URY\",\n   \"name\": \"Item-naming_series-hidden\",\n   \"owner\": \"sanjusha@tridz.com\",\n   \"property\": \"hidden\",\n   \"property_type\": \"Check\",\n   \"row_name\": null,\n   \"value\": \"1\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2026-03-30 22:04:59.943718\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"naming_series\",\n   \"idx\": 0,\n   \"is_system_generated\": 1,\n   \"modified\": \"2026-03-30 22:04:59.943718\",\n   \"modified_by\": \"Administrator\",\n   \"module\": null,\n   \"name\": \"Item-naming_series-options\",\n   \"owner\": \"Administrator\",\n   \"property\": \"options\",\n   \"property_type\": \"Text\",\n   \"row_name\": null,\n   \"value\": \"STO-ITEM-.YYYY.-\"\n  },\n  {\n   \"_assign\": null,\n   \"_comments\": null,\n   \"_liked_by\": null,\n   \"_user_tags\": null,\n   \"creation\": \"2025-07-03 18:00:03.679179\",\n   \"default_value\": null,\n   \"doc_type\": \"Item\",\n   \"docstatus\": 0,\n   \"doctype_or_field\": \"DocField\",\n   \"field_name\": \"naming_series\",\n   \"idx\": 0,\n   \"is_system_generated\": 1,\n   \"modified\": \"2025-07-03 18:00:03.679179\",\n   \"modified_by\": \"sanjusha@tridz.com\",\n   \"module\": \"URY\",\n   \"name\": \"Item-naming_series-reqd\",\n   \"owner\": \"sanjusha@tridz.com\",\n   \"property\": \"reqd\",\n   \"property_type\": \"Check\",\n   \"row_name\": null,\n   \"value\": \"0\"\n  }\n ],\n \"sync_on_migrate\": 1\n}"
  },
  {
    "path": "ury/ury/doctype/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/aggregator_settings/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/aggregator_settings/aggregator_settings.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2024-08-13 12:50:02.398001\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"customer\",\n  \"price_list\",\n  \"mode_of_payments\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"customer\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Customer\",\n   \"options\": \"Customer\",\n   \"width\": \"2\"\n  },\n  {\n   \"fieldname\": \"price_list\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Default Price List\",\n   \"options\": \"Price List\",\n   \"width\": \"2\"\n  },\n  {\n   \"fieldname\": \"mode_of_payments\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Default Mode Of Payments\",\n   \"options\": \"Mode of Payment\",\n   \"width\": \"2\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-07-25 10:53:57.357541\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Aggregator Settings\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"row_format\": \"Dynamic\",\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/aggregator_settings/aggregator_settings.py",
    "content": "# Copyright (c) 2024, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass AggregatorSettings(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/item_add_on/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/item_add_on/item_add_on.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2025-07-25 18:42:32.997931\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"item\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item\",\n   \"options\": \"Item\",\n   \"width\": \"2\"\n  }\n ],\n \"grid_page_length\": 50,\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-07-25 18:43:20.484388\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Item Add On\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"row_format\": \"Dynamic\",\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/item_add_on/item_add_on.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass ItemAddOn(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/menu_for_room/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/menu_for_room/menu_for_room.js",
    "content": "// Copyright (c) 2024, Tridz Technologies Pvt. Ltd and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('Menu for Room', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/menu_for_room/menu_for_room.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2024-01-07 13:11:04.784852\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"menu\",\n  \"room\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"menu\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Menu\",\n   \"options\": \"URY Menu\"\n  },\n  {\n   \"fieldname\": \"room\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Room\",\n   \"options\": \"URY Room\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2024-01-07 13:11:45.463070\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Menu for Room\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/menu_for_room/menu_for_room.py",
    "content": "# Copyright (c) 2024, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass MenuforRoom(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/menu_for_room/test_menu_for_room.py",
    "content": "# Copyright (c) 2024, Tridz Technologies Pvt. Ltd and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestMenuforRoom(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/multiple_rooms/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/multiple_rooms/multiple_rooms.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2025-03-25 13:48:29.430246\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"room\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"room\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Room\",\n   \"options\": \"URY Room\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-03-25 13:51:14.466647\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Multiple Rooms\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/multiple_rooms/multiple_rooms.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass MultipleRooms(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/order_type_menu/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/order_type_menu/order_type_menu.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2025-03-06 10:26:48.242529\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"order_type\",\n  \"menu\"\n ],\n \"fields\": [\n  {\n   \"columns\": 5,\n   \"fieldname\": \"order_type\",\n   \"fieldtype\": \"Select\",\n   \"in_list_view\": 1,\n   \"label\": \"Order Type\",\n   \"options\": \"\\nPhone In\\nTake Away\\nDelivery\"\n  },\n  {\n   \"columns\": 5,\n   \"fieldname\": \"menu\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Menu\",\n   \"options\": \"URY Menu\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-03-06 10:26:48.242529\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Order Type Menu\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/order_type_menu/order_type_menu.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass OrderTypeMenu(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/pos_item_variants/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/pos_item_variants/pos_item_variants.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2025-07-25 18:52:38.763971\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"item\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item\",\n   \"options\": \"Item\",\n   \"width\": \"2\"\n  }\n ],\n \"grid_page_length\": 50,\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-07-25 18:53:21.573507\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"POS Item Variants\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"row_format\": \"Dynamic\",\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/pos_item_variants/pos_item_variants.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass POSItemVariants(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/role_permitted/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/role_permitted/role_permitted.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2024-01-04 11:00:27.812098\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"role\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"role\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Role\",\n   \"options\": \"Role\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2024-01-04 11:09:28.614882\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Role Permitted\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/role_permitted/role_permitted.py",
    "content": "# Copyright (c) 2024, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass RolePermitted(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing/sub_pos_closing.js",
    "content": "// Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n// For license information, please see license.txt\n\n// frappe.ui.form.on(\"Sub POS Closing\", {\n// \trefresh(frm) {\n\n// \t},\n// });\n// Copyright (c) 2024, Tridz and contributors\n// For license information, please see license.txt\nvar ttl;\nvar branch = \"\"\nfunction padNumber(num) {\n\treturn num.toString().padStart(2, '0');\n}\nfrappe.ui.form.on(\"Sub POS Closing\", {\n\n\n\tonload: function (frm) {\n        \t\n\t\tfrm.set_value(\"user\", frappe.session.user);\n        frappe.call({\n            method: 'ury.ury.doctype.sub_pos_closing.sub_pos_closing.get_pos_profile',\n            callback: function(r) {\n                if (r.message) {\n                    frm.set_value('pos_profile', r.message);\n                }\n            }\n        });\n\t\tfrm.set_query(\"user\", function (doc) {\n\t\t\treturn {\n\t\t\t\tquery: \"ury.ury.doctype.sub_pos_closing.sub_pos_closing.get_cashiers\",\n\t\t\t\tfilters: { parent: doc.pos_profile },\n\t\t\t};\n\t\t});\n        user = frappe.session.user\n\t\tfrm.set_query(\"pos_opening_entry\", function (doc) {\n\t\t\treturn { filters: { status: 'Open', docstatus: 1,user:user } };\n\t\t});\n\n\t\tif (frm.doc.docstatus === 0 && !frm.doc.amended_from)\n\t\t\tfrm.set_value(\"period_end_date\", frappe.datetime.now_datetime());\n\n\t\tfrappe.realtime.on(\"closing_process_complete\", async function (data) {\n\t\t\tawait frm.reload_doc();\n\t\t\tif (frm.doc.status == \"Failed\" && frm.doc.error_message) {\n\t\t\t\tfrappe.msgprint({\n\t\t\t\t\ttitle: __(\"Sub POS Closing Failed\"),\n\t\t\t\t\tmessage: frm.doc.error_message,\n\t\t\t\t\tindicator: \"orange\",\n\t\t\t\t\tclear: true,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\n\t\tif (frm.doc.docstatus == 1) {\n\t\t\tif (!frm.doc.posting_date) {\n\t\t\t\tfrm.set_value(\"posting_date\", frappe.datetime.nowdate());\n\t\t\t}\n\t\t\tif (!frm.doc.posting_time) {\n\t\t\t\tfrm.set_value(\"posting_time\", frappe.datetime.now_time());\n\t\t\t}\n\t\t}\n\n\n\t},\n\n\trefresh: function (frm) {\n\n\t\tif (frm.doc.docstatus == 1 && frm.doc.status == \"Failed\") {\n\t\t\tconst issue = '<a id=\"jump_to_error\" style=\"text-decoration: underline;\">issue</a>';\n\t\t\tfrm.dashboard.set_headline(\n\t\t\t\t__(\n\t\t\t\t\t\"Sub POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.\",\n\t\t\t\t\t[issue]\n\t\t\t\t)\n\t\t\t);\n\n\t\t\t$(\"#jump_to_error\").on(\"click\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tfrappe.utils.scroll_to(cur_frm.get_field(\"error_message\").$wrapper, true, 30);\n\t\t\t});\n\n\t\t\tfrm.add_custom_button(__(\"Retry\"), function () {\n\t\t\t\tfrm.call(\"retry\", {}, () => {\n\t\t\t\t\tfrm.reload_doc();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t},\n\n\tpos_opening_entry(frm) {\n\t\tif (\n\t\t\tfrm.doc.pos_opening_entry &&\n\t\t\tfrm.doc.period_start_date &&\n\t\t\tfrm.doc.period_end_date &&\n\t\t\tfrm.doc.user\n\t\t) {\n\t\t\treset_values(frm);\n\t\t\tfrappe.run_serially([\n\t\t\t\t() => frappe.dom.freeze(__(\"Loading Invoices! Please Wait...\")),\n\t\t\t\t() => frm.trigger(\"set_opening_amounts\"),\n\t\t\t\t() => frm.trigger(\"get_pos_invoices\"),\n\t\t\t\t() => frappe.dom.unfreeze(),\n\t\t\t]);\n\t\t}\n\t},\n\n\tset_opening_amounts(frm) {\n\t\treturn frappe.db\n\t\t\t.get_doc(\"POS Opening Entry\", frm.doc.pos_opening_entry)\n\t\t\t.then(({ balance_details }) => {\n\n\t\t\t\tbalance_details.forEach((detail) => {\n\t\t\t\t\tfrm.add_child(\"payment_reconciliation\", {\n\t\t\t\t\t\tmode_of_payment: detail.mode_of_payment,\n\t\t\t\t\t\topening_amount: detail.opening_amount,\n\t\t\t\t\t\texpected_amount: detail.opening_amount,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t},\n\n\tget_pos_invoices(frm) {\n\t\treturn frappe.call({\n\t\t\tmethod: \"ury.ury.doctype.sub_pos_closing.sub_pos_closing.get_pos_invoices\",\n\t\t\targs: {\n\t\t\t\tstart: frappe.datetime.get_datetime_as_string(frm.doc.period_start_date),\n\t\t\t\tend: frappe.datetime.get_datetime_as_string(frm.doc.period_end_date),\n\t\t\t\tpos_profile: frm.doc.pos_profile,\n\t\t\t\tuser: frm.doc.user,\n\t\t\t},\n\t\t\tcallback: (r) => {\n\t\t\t\tlet pos_docs = r.message;\n\t\t\t\tset_form_data(pos_docs, frm);\n\t\t\t\trefresh_fields(frm);\n\t\t\t},\n\t\t});\n\t},\n\n\tbefore_save: async function (frm) {\n\t\tfrappe.dom.freeze(__(\"Processing Sales! Please Wait...\"));\n\t\tfrm.set_value(\"grand_total\", 0);\n\t\tfrm.set_value(\"net_total\", 0);\n\t\tfrm.set_value(\"total_quantity\", 0);\n\n\t\tfor (let row of frm.doc.payment_reconciliation) {\n\t\t\trow.expected_amount = row.opening_amount;\n\t\t}\n\n\t\tawait Promise.all([\n\t\t\tfrappe.call({\n\t\t\t\tmethod: \"ury.ury.doctype.sub_pos_closing.sub_pos_closing.get_pos_invoices\",\n\t\t\t\targs: {\n\t\t\t\t\tstart: frappe.datetime.get_datetime_as_string(frm.doc.period_start_date),\n\t\t\t\t\tend: frappe.datetime.get_datetime_as_string(frm.doc.period_end_date),\n\t\t\t\t\tpos_profile: frm.doc.pos_profile,\n\t\t\t\t\tuser: frm.doc.user,\n\t\t\t\t},\n\t\t\t\tcallback: (r) => {\n\t\t\t\t\tlet pos_invoices = r.message;\n\t\t\t\t\tfor (let doc of pos_invoices) {\n\t\t\t\t\t\tfrm.doc.grand_total += flt(doc.grand_total);\n\t\t\t\t\t\tfrm.doc.net_total += flt(doc.net_total);\n\t\t\t\t\t\tfrm.doc.total_quantity += flt(doc.total_qty);\n\t\t\t\t\t\trefresh_payments(doc, frm);\n\t\t\t\t\t\trefresh_fields(frm);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}),\n\t\t]);\n\t\tfrappe.dom.unfreeze();\n\t},\n});\n\nfrappe.ui.form.on('Sub POS Closing Payment', {\n\tclosing_amount: (frm, cdt, cdn) => {\n\t\tconst row = locals[cdt][cdn];\n\t\tfrappe.model.set_value(cdt, cdn, \"difference\", flt(row.closing_amount - row.expected_amount));\n\t}\n})\nfunction set_form_data(data, frm) {\n\tdata.forEach(d => {\n\t\trefresh_payments(d, frm);\n\t\tadd_to_pos_transaction(d, frm);\n\t\tfrm.doc.grand_total += flt(d.grand_total);\n\t\tfrm.doc.net_total += flt(d.net_total);\n\t\tfrm.doc.total_quantity += flt(d.total_qty);\n\t\t\n\t});\n}\nfunction add_to_pos_transaction(d, frm) {\n\tfrm.add_child(\"pos_transactions\", {\n\t\tpos_invoice: d.name,\n\t\tposting_date: d.posting_date,\n\t\tgrand_total: d.grand_total\n\t});\n}\n\n\nfunction refresh_payments(d, frm) {\n\td.payments.forEach(p => {\n\t\tconst payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);\n\t\tif (p.account == d.account_for_change_amount) {\n\t\t\tp.amount -= flt(d.change_amount);\n\t\t}\n\t\tif (payment) {\n\t\t\tpayment.expected_amount += flt(p.amount);\n\t\t\tpayment.difference = payment.closing_amount - payment.expected_amount;\n\t\t} else {\n\t\t\tfrm.add_child(\"payment_reconciliation\", {\n\t\t\t\tmode_of_payment: p.mode_of_payment,\n\t\t\t\topening_amount: 0,\n\t\t\t\texpected_amount: p.amount,\n\t\t\t\tclosing_amount: 0\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunction reset_values(frm) {\n\tfrm.set_value(\"pos_transactions\", []);\n\tfrm.set_value(\"payment_reconciliation\", []);\n}\n\nfunction refresh_fields(frm) {\n\tfrm.refresh_field(\"pos_transactions\");\n\tfrm.refresh_field(\"payment_reconciliation\");\n}"
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing/sub_pos_closing.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"autoname\": \"SUB-CLO-.YYYY.-.#####\",\n \"creation\": \"2025-03-26 03:40:02.290820\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"period_details_section\",\n  \"period_start_date\",\n  \"period_end_date\",\n  \"column_break_nws7\",\n  \"posting_date\",\n  \"posting_time\",\n  \"pos_opening_entry\",\n  \"status\",\n  \"user_details_section\",\n  \"company\",\n  \"column_break_nahd\",\n  \"pos_profile\",\n  \"user\",\n  \"linked_invoices_section\",\n  \"pos_transactions\",\n  \"section_break_xcg0\",\n  \"payment_reconciliation\",\n  \"grand_total\",\n  \"net_total\",\n  \"total_quantity\",\n  \"amended_from\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"period_details_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Period Details\"\n  },\n  {\n   \"fetch_from\": \"pos_opening_entry.period_start_date\",\n   \"fieldname\": \"period_start_date\",\n   \"fieldtype\": \"Datetime\",\n   \"in_list_view\": 1,\n   \"label\": \"Period Start Date\",\n   \"read_only\": 1,\n   \"reqd\": 1\n  },\n  {\n   \"default\": \"Today\",\n   \"fieldname\": \"period_end_date\",\n   \"fieldtype\": \"Datetime\",\n   \"in_list_view\": 1,\n   \"label\": \"Period End Date\",\n   \"read_only\": 1,\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"column_break_nws7\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"default\": \"Today\",\n   \"fieldname\": \"posting_date\",\n   \"fieldtype\": \"Date\",\n   \"in_list_view\": 1,\n   \"label\": \"Posting Date\",\n   \"reqd\": 1\n  },\n  {\n   \"default\": \"Now\",\n   \"fieldname\": \"posting_time\",\n   \"fieldtype\": \"Time\",\n   \"label\": \"Posting Time\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"pos_opening_entry\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"POS Opening Entry\",\n   \"options\": \"POS Opening Entry\",\n   \"reqd\": 1\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"default\": \"Draft\",\n   \"fieldname\": \"status\",\n   \"fieldtype\": \"Select\",\n   \"hidden\": 1,\n   \"label\": \"Status\",\n   \"options\": \"Draft\\nSubmitted\\nQueued\\nFailed\\nCancelled\",\n   \"print_hide\": 1,\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"user_details_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"User Details\"\n  },\n  {\n   \"fetch_from\": \"pos_opening_entry.company\",\n   \"fieldname\": \"company\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Company\",\n   \"options\": \"Company\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"column_break_nahd\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"pos_profile\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"POS Profile \",\n   \"options\": \"POS Profile\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"user\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Cashier\",\n   \"options\": \"User\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"linked_invoices_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Linked Invoices\"\n  },\n  {\n   \"fieldname\": \"pos_transactions\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"POS Transactions\",\n   \"options\": \"Sub POS Invoices\"\n  },\n  {\n   \"fieldname\": \"section_break_xcg0\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"fieldname\": \"payment_reconciliation\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Payment Reconciliation\",\n   \"options\": \"Sub POS Closing Payment\"\n  },\n  {\n   \"fieldname\": \"grand_total\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Grand Total\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"net_total\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Net Total\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"total_quantity\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Total Quantity\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"amended_from\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Amended From\",\n   \"no_copy\": 1,\n   \"options\": \"Sub POS Closing\",\n   \"print_hide\": 1,\n   \"read_only\": 1,\n   \"search_index\": 1\n  },\n  {\n   \"fieldname\": \"amended_from\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Amended From\",\n   \"no_copy\": 1,\n   \"options\": \"Sub POS Closing\",\n   \"print_hide\": 1,\n   \"read_only\": 1,\n   \"search_index\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"is_submittable\": 1,\n \"links\": [],\n \"modified\": \"2025-04-02 18:58:48.902980\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Sub POS Closing\",\n \"naming_rule\": \"Expression (old style)\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"share\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  },\n  {\n   \"cancel\": 1,\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  },\n  {\n   \"permlevel\": 1,\n   \"read\": 1,\n   \"role\": \"URY Manager\"\n  },\n  {\n   \"create\": 1,\n   \"read\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  },\n  {\n   \"read\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1\n  }\n ],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": [],\n \"track_changes\": 1\n}"
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing/sub_pos_closing.py",
    "content": "\nimport frappe\nfrom frappe import _\nfrom frappe.utils import flt, get_datetime\nfrom datetime import datetime, timedelta\nfrom frappe.model.document import Document\nimport json\nimport requests\nfrom datetime import datetime\nfrom ury.ury_pos.api import getBranch\nfrom frappe.utils import  get_datetime,now\n\n\nclass SubPOSClosing(Document):\n    def validate(self):\n        owner = None\n        branch = frappe.db.get_value(\"POS Profile\", self.pos_profile, \"branch\")\n\n        draft_invoices = frappe.get_all(\n            \"POS Invoice\",\n            fields=[\"name\"],\n            filters={\"branch\": branch, \"status\": \"Draft\", \"docstatus\": \"0\",\"cashier\":self.user},\n        )\n        if draft_invoices:\n            frappe.throw(\"Submit/Delete Draft Invoices\")\n\n        date_time = now()\n        if isinstance(date_time, str):\n            formatted_date_time = date_time.split('.')[0]\n        else:\n            formatted_date_time = date_time.strftime('%Y-%m-%d %H:%M:%S')\n        self.period_end_date = date_time\n\n        time_part = formatted_date_time.split(' ')[1]\n        self.posting_time = time_part\n        \n        invoices = frappe.get_all(\n            \"POS Invoice\",\n            filters={\n                \"docstatus\": 1,\n                \"status\":\"Paid\",\n                \"posting_date\": [\"between\", [self.period_start_date, self.period_end_date]],\n                \"cashier\":self.user\n            },\n            fields=[\"name\", \"posting_date\", \"customer\", \"grand_total\", \"base_grand_total\"]\n        )\n        \n        self.set(\"pos_transactions\", [])\n        \n        for invoice in invoices:\n            self.append(\"pos_transactions\", {\n                \"pos_invoice\": invoice.name,\n                \"posting_date\": invoice.posting_date,\n                \"customer\": invoice.customer,\n                \"grand_total\": invoice.grand_total,\n                \"base_grand_total\": invoice.base_grand_total\n            })\n\n        multiple_cashier = frappe.db.get_value(\"POS Profile\", self.pos_profile, \"custom_enable_multiple_cashier\")\n        if multiple_cashier:\n            get_cashier = frappe.get_doc(\"POS Profile\", self.pos_profile)\n            for user_details in get_cashier.applicable_for_users:\n                if user_details.custom_main_cashier:\n                    owner = user_details.user\n            if frappe.session.user == owner:\n                frappe.throw(\"The Main Cashier cannot close a Sub POS Closing entry.\")\n        else:\n            pass\n    \n    def on_submit(self):\n        opening_entry = frappe.get_doc(\"POS Opening Entry\", self.pos_opening_entry)\n        opening_entry.custom_sub_pos_close = self.name\n        opening_entry.status = \"Closed\"\n        opening_entry.save()\n    \n    def on_cancel(self):\n        opening_entry = frappe.get_doc(\"POS Opening Entry\", self.pos_opening_entry)\n        opening_entry.custom_sub_pos_close = self.name\n        opening_entry.status = \"Open\"\n        opening_entry.save()\n\n\n@frappe.whitelist()\ndef get_pos_profile():\n    branch = getBranch()\n    pos_profile = frappe.db.get_value(\"POS Profile\", {\"branch\": branch}, \"name\")\n    return pos_profile\n\n\n@frappe.whitelist()\n@frappe.validate_and_sanitize_search_inputs\ndef get_cashiers(doctype, txt, searchfield, start, page_len, filters):\n    cashiers_list = frappe.get_all(\n        \"POS Profile User\", filters=filters, fields=[\"user\"], as_list=1\n    )\n    return [c for c in cashiers_list]\n\n\n@frappe.whitelist()\ndef get_pos_invoices(start, end, pos_profile, user):\n    data = frappe.db.sql(\n        \"\"\"\n        select\n            name, timestamp(posting_date, posting_time) as \"timestamp\"\n        from\n            `tabPOS Invoice`\n        where\n            cashier = %s and docstatus = 1 and pos_profile = %s and ifnull(consolidated_invoice,'') = '' and status != \"Consolidated\"\n        \"\"\",\n        (user, pos_profile),\n        as_dict=1,\n    )\n\n    data = list(\n        filter(\n            lambda d: get_datetime(start)\n            <= get_datetime(d.timestamp)\n            <= get_datetime(end),\n            data,\n        )\n    )\n    # need to get taxes and payments so can't avoid get_doc\n    data = [frappe.get_doc(\"POS Invoice\", d.name).as_dict() for d in data]\n\n    return data"
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing/test_sub_pos_closing.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestSubPOSClosing(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing_payment/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing_payment/sub_pos_closing_payment.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2025-03-26 03:38:51.845091\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"mode_of_payment\",\n  \"opening_amount\",\n  \"expected_amount\",\n  \"closing_amount\",\n  \"difference\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"mode_of_payment\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Mode of Payment\",\n   \"options\": \"Mode of Payment\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"opening_amount\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Opening Amount\",\n   \"options\": \"company:company_currency\"\n  },\n  {\n   \"fieldname\": \"expected_amount\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Expected Amount\",\n   \"options\": \"company:company_currency\",\n   \"permlevel\": 1\n  },\n  {\n   \"fieldname\": \"closing_amount\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Closing Amount\"\n  },\n  {\n   \"fieldname\": \"difference\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Difference\",\n   \"options\": \"company:company_currency\",\n   \"permlevel\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-03-26 03:38:51.845091\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Sub POS Closing Payment\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/sub_pos_closing_payment/sub_pos_closing_payment.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass SubPOSClosingPayment(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/sub_pos_invoices/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/sub_pos_invoices/sub_pos_invoices.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2025-03-26 03:39:26.809818\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"pos_invoice_section\",\n  \"pos_invoice\",\n  \"posting_date\",\n  \"grand_total\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"pos_invoice_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"POS Invoice\"\n  },\n  {\n   \"fieldname\": \"pos_invoice\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"POS Invoice\",\n   \"options\": \"POS Invoice\",\n   \"width\": \"3\"\n  },\n  {\n   \"fieldname\": \"posting_date\",\n   \"fieldtype\": \"Date\",\n   \"in_list_view\": 1,\n   \"label\": \"Date\",\n   \"width\": \"3\"\n  },\n  {\n   \"fieldname\": \"grand_total\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Amount\",\n   \"width\": \"3\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-03-26 03:39:26.809818\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Sub POS Invoices\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/sub_pos_invoices/sub_pos_invoices.py",
    "content": "# Copyright (c) 2025, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass SubPOSInvoices(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_cost_of_goods/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_cost_of_goods/ury_cost_of_goods.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-10-05 11:50:56.628931\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item_code\",\n  \"item_name\",\n  \"item_group\",\n  \"qty\",\n  \"buying_price\",\n  \"amount\"\n ],\n \"fields\": [\n  {\n   \"columns\": 2,\n   \"fieldname\": \"item_code\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item\",\n   \"options\": \"Item\",\n   \"reqd\": 1\n  },\n  {\n   \"columns\": 2,\n   \"fetch_from\": \"item_code.item_name\",\n   \"fieldname\": \"item_name\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Item Name\",\n   \"reqd\": 1\n  },\n  {\n   \"columns\": 2,\n   \"fetch_from\": \"item_code.item_group\",\n   \"fieldname\": \"item_group\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item Group\",\n   \"options\": \"Item Group\",\n   \"reqd\": 1\n  },\n  {\n   \"columns\": 1,\n   \"fieldname\": \"qty\",\n   \"fieldtype\": \"Float\",\n   \"in_list_view\": 1,\n   \"label\": \"Quantity\",\n   \"reqd\": 1\n  },\n  {\n   \"columns\": 1,\n   \"fieldname\": \"buying_price\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Buying Price\",\n   \"precision\": \"2\",\n   \"reqd\": 1\n  },\n  {\n   \"columns\": 2,\n   \"fieldname\": \"amount\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Amount\",\n   \"precision\": \"2\",\n   \"reqd\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2024-11-26 11:22:09.267658\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Cost Of Goods\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_cost_of_goods/ury_cost_of_goods.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYCostOfGoods(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_daily_p_and_l/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_daily_p_and_l/profit_loss_details.html",
    "content": "<div class=\"clearfix\"></div>\n<div class=\"box\" style=\"background-color: #f5f5f5; border-radius: 10px; padding: 20px;\">\n    <div class=\"grid-body\">\n        <div class=\"rows text-center\">\n            <!-- Sales summary section -->\n            <div style=\"border: 1px solid #ccc; border-radius: 5px; padding: 20px; background-color: #fff;\">\n                <h3 class=\"ellipsis\" style=\"color: rgb(0, 0, 0);\">{{ data.branch }}</h3>\n                <h4 class=\"ellipsis\" style=\"color: rgb(0, 0, 0);\">Date: {{ data.date }}</h4>\n                <div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n                    <style>\n                        table {\n                            border-collapse: separate;\n                            width: 100%;\n                            background-color: #f9f9f9; /* Adjust the background color of the table */\n                        }\n\n                        table td.text-right {\n                            background-color: rgb(218, 227, 243);\n                        }\n\n                        th, td {\n                            padding: 10px;\n                            text-align: right;\n                        }\n\n                        th {\n                            text-align: left;\n                            font-weight: bold;\n                        }\n\n                        .text-left {\n                            text-align: left;\n                        }\n\n                        .font-bold {\n                            font-weight: bold;\n                        }\n\n                        .bg-green {\n                            background-color: rgb(146, 208, 80);\n                        }\n\n                        .bg-gray {\n                            background-color: rgb(201, 201, 201);\n                        }\n                    </style>\n                    <table class=\"table table-bordered table-hover\">\n                        <thead>\n                            <tr>\n                                <th class=\"text-left font-bold\">{{ _(\"REVENUE\") }}</th>\n                                <th class=\"text-right font-bold\">{{ _(\"AMOUNT\") }}</th>\n                                <th class=\"text-right font-bold\">{{ _(\"PERCENTAGE\") }}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Gross Sales') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.gross_sales or '', currency=currency) }}</td>\n                                <td>{{ data.gross_sales_percent }}%</td>\n                            </tr>\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Discounts & Round Offs') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.cash_discount_round_off or '', currency=currency) }}</td>\n                                <td>{{ data.cash_discount_round_off_percent }}%</td>\n                            </tr>\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Tax') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.tax or '', currency=currency) }}</td>\n                                <td>{{ data.tax_percent }}%</td>\n                            </tr>\n                            <tr class=\"bg-green\">\n                                <td class=\"text-left font-bold\">{{ _('Net Sales') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.net_sales or '', currency=currency) }}</td>\n                                <td>{{ data.net_sales_percent }}%</td>\n                            </tr>\n                            <tr class=\"bg-gray\">\n                                <td class=\"text-left font-bold\">{{ _('Cost of Goods Sold') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.cogs or '', currency=currency) }}</td>\n                                <td>{{ data.cogs_percent }}%</td>\n                            </tr>\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Direct Expenses') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.total_direct_expenses or '', currency=currency) }}</td>\n                                <td>{{ data.total_direct_expenses_percent }}%</td>\n                            </tr>\n                            <tr class=\"bg-green\">\n                                <td class=\"text-left font-bold\">{{ _('Gross Profit/Loss') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.gross_profit or '', currency=currency) }}</td>\n                                <td>{{ data.gross_profit_percent }}%</td>\n                            </tr>\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Employee Cost') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.total_employee_costs or '', currency=currency) }}</td>\n                                <td>{{ data.total_employee_costs_percent }}%</td>\n                            </tr>\n                            {% for expense in data.indirect_expenses_breakup %}\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ expense.breakup }}</td>\n                                <td>{{ expense.amount }}</td>\n                                <td>{{ expense.percent }}%</td>\n                            </tr>\n                            {% endfor %}\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Other Expenses') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.total_other_expenses or '', currency=currency) }}</td>\n                                <td>{{ data.other_expenses_percent }}%</td>\n                            </tr>\n                            <tr>\n                                <td class=\"text-left font-bold\">{{ _('Depreciation') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.depreciation or '', currency=currency) }}</td>\n                                <td>{{ data.depreciation_percent }}%</td>\n                            </tr>\n                            <tr class=\"bg-gray\">\n                                <td class=\"text-left font-bold\">{{ _('Total Indirect Expenses') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.total_indirect_expenses or '', currency=currency) }}</td>\n                                <td>{{ data.total_indirect_expenses_percent }}%</td>\n                            </tr>\n                            <tr class=\"bg-green\">\n                                <td class=\"text-left font-bold\">{{ _('Net Profit/Loss') }}</td>\n                                <td>{{ frappe.utils.fmt_money(data.net_profit or '', currency=currency) }}</td>\n                                <td>{{ data.net_profit_percent }}%</td>\n                            </tr>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "ury/ury/doctype/ury_daily_p_and_l/test_ury_daily_p_and_l.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYDailyPandL(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_daily_p_and_l/ury_daily_p_and_l.js",
    "content": "// Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY Daily P and L', {\n\trefresh: function(frm) {\n\t\tfrm.page.wrapper.find(\".comment-box\").css({'display':'none'});\n\t\t//restrict adding rows\n\t\tfrm.set_df_property('materials_consumed', 'cannot_add_rows', true);\n\t\t//restrict deleting rows\n\t\tfrm.set_df_property('materials_consumed', 'cannot_delete_rows', true);\n\t\tif(frm.doc.docstatus === 1){\n\t\t\tset_html_data(frm)\n\t\t\t$('#ury-daily-p-and-l-profit_loss_tab-tab').click();\n\t\t}\n\t},\n\tonload: function(frm) {\n\t\tif (frm.doc.remarks !== \"\" && frm.doc.docstatus === 0){\n\t\t\tfrm.set_value(\"remarks\",\"\");\n\t\t}\n\t},\n\tbranch: function(frm){\n\t\tif(frm.doc.branch === \"\"){\n\t\t\tfrm.set_value(\"materials_consumed\",[])\n\t\t}\n\t\telse{\n\t\t\tfrappe.db.exists('URY Report Settings', frm.doc.branch).then(report_settings_exist => {\n\t\t\t\tif(report_settings_exist){\n\t\t\t\t\tfrm.set_value(\"materials_consumed\",[])\n\t\t\t\t\tfrappe.db.get_doc('URY Report Settings', frm.doc.branch).then(report_settings => {\n\t\t\t\t\t\t// Access the child table data from Report Settings\n\t\t\t\n\t\t\t\t\t\treport_settings.consumables.forEach(materials_data_item => {\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tconst newRow = frappe.model.add_child(frm.doc, 'materials_consumed');\n\t\t\t\t\n\t\t\t\t\t\t\tnewRow.material = materials_data_item.material;\n\t\t\t\t\t\t\tnewRow.cost_per_unit = materials_data_item.cost_per_unit;\n\t\t\t\t\t\t\tnewRow.material = materials_data_item.material;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// Save the current document\n\t\t\t\t\t\t\tfrm.refresh_field('materials_consumed');\n\t\t\t\t\t\t})\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse{\n\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\ttitle: __(\"Missing URY Report Settings\"),\n\t\t\t\t\t\tmessage: __(\"Please set up URY Report Settings for the selected branch: <strong>{0}</strong>\", [frm.doc.branch]),\n\t\t\t\t\t});\t\t\t\t\t\n\t\t\t\t\tfrm.set_value(\"materials_consumed\",[])\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t},\n\tdate: function(frm){\n\t\tset_electricity_closing(frm)\n\t}\n});\n\nfunction set_html_data(frm) {\n\tif (frm.doc.docstatus === 1) {\n\t\tfrappe.call({\n\t\t\tmethod: \"get_proft_loss_details\",\n\t\t\tdoc: frm.doc,\n\t\t\tcallback: (r) => {\n\t\t\t\tfrm.get_field(\"proft_loss_details\").$wrapper.html(r.message);\n\t\t\t}\n\t\t});\n\t}\n}\n\nfunction set_electricity_closing(frm) {\n\ttry{\n\t\tvar current_date_obj = new Date(frm.doc.date);\n\n\t\tcurrent_date_obj.setDate(current_date_obj.getDate() - 1);\n\n\t\tvar previous_date = current_date_obj.toISOString().split('T')[0];\n\t\tif (frm.doc.branch && frm.doc.date){\n\t\t\tfrappe.call({\n\t\t\t\tmethod: 'frappe.client.get_list',\n\t\t\t\targs: {\n\t\t\t\t\tdoctype: 'URY Daily P and L',\n\t\t\t\t\tfields: ['electricity_closing'],\n\t\t\t\t\tfilters: {\n\t\t\t\t\t\t'branch': frm.doc.branch,\n\t\t\t\t\t\t'date': previous_date\n\t\t\t\t\t},\n\t\t\t\t\torder_by: 'creation desc',\n\t\t\t\t},\n\t\t\t\tcallback: function(response) {\n\t\t\t\t\tif (response.message && response.message.length > 0) {\n\t\t\t\t\t\tfrm.set_value(\"electricity_opening\",response.message[0].electricity_closing)\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tfrm.set_value(\"electricity_opening\",\"\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\tcatch(err){\n\t\tconsole.log(\"Last P and L not found\")\n\t}\n}\n\nfrappe.ui.form.on('URY P and L Materials', {\n\tunits_consumed: function(frm, cdt, cdn) {\n\t\tvar row = locals[cdt][cdn];\n\t\trow.amount = row.cost_per_unit*row.units_consumed\n\t\tfrm.refresh_fields();\n\t}\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_daily_p_and_l/ury_daily_p_and_l.json",
    "content": "{\n \"actions\": [],\n \"autoname\": \"Daily-PL-.YYYY.-.#####\",\n \"creation\": \"2023-10-05 16:43:50.108202\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"profit_loss_tab\",\n  \"proft_loss_details\",\n  \"p_and_l_section\",\n  \"gross_sales\",\n  \"cash_discount_round_off\",\n  \"tax\",\n  \"net_sales\",\n  \"cogs\",\n  \"total_direct_expenses\",\n  \"gross_profit\",\n  \"total_employee_costs\",\n  \"depreciation\",\n  \"total_other_expenses\",\n  \"total_indirect_expenses\",\n  \"net_profit\",\n  \"column_break_bqo3d\",\n  \"gross_sales_percent\",\n  \"cash_discount_round_off_percent\",\n  \"tax_percent\",\n  \"net_sales_percent\",\n  \"cogs_percent\",\n  \"total_direct_expenses_percent\",\n  \"gross_profit_percent\",\n  \"total_employee_costs_percent\",\n  \"depreciation_percent\",\n  \"other_expenses_percent\",\n  \"total_indirect_expenses_percent\",\n  \"net_profit_percent\",\n  \"details_tab\",\n  \"branch\",\n  \"column_break_so5qh\",\n  \"date\",\n  \"section_break_qpbbd\",\n  \"electricity_opening\",\n  \"column_break_mq2bv\",\n  \"electricity_closing\",\n  \"section_break_6mqsm\",\n  \"materials_consumed\",\n  \"section_break_dv4iu\",\n  \"other_expenses\",\n  \"remarks\",\n  \"expenses_breakup\",\n  \"direct_expenses_breakup\",\n  \"section_break_lught\",\n  \"employee_costs_breakup\",\n  \"section_break_rhkwy\",\n  \"indirect_expenses_breakup\",\n  \"cogs_tab\",\n  \"cost_of_goods\",\n  \"amended_from\"\n ],\n \"fields\": [\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"proft_loss_details\",\n   \"fieldtype\": \"HTML\"\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"reqd\": 1,\n   \"set_only_once\": 1\n  },\n  {\n   \"fieldname\": \"column_break_so5qh\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"date\",\n   \"fieldtype\": \"Date\",\n   \"in_list_view\": 1,\n   \"label\": \"Date\",\n   \"reqd\": 1,\n   \"set_only_once\": 1\n  },\n  {\n   \"fieldname\": \"section_break_qpbbd\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"description\": \"opening reading(Unit)\",\n   \"fieldname\": \"electricity_opening\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Electricity Opening\",\n   \"precision\": \"2\",\n   \"read_only_depends_on\": \"eval:doc.docstatus==1\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"column_break_mq2bv\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"description\": \"closing reading(Unit)\",\n   \"fieldname\": \"electricity_closing\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Electricity Closing\",\n   \"precision\": \"2\",\n   \"read_only_depends_on\": \"eval:doc.docstatus==1\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"other_expenses\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Other Expenses\",\n   \"options\": \"URY P and L Breakup\",\n   \"read_only_depends_on\": \"eval:doc.docstatus==1\"\n  },\n  {\n   \"collapsible\": 1,\n   \"fieldname\": \"p_and_l_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Summary\"\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"gross_sales\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Gross Sales\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"gross_sales_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Gross Sales Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"cash_discount_round_off\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Discounts & Round Offs\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"cash_discount_round_off_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Discounts & Round Offs Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"tax\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Tax\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"tax_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Tax Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"net_sales\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Net Sales\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"net_sales_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Net Sales Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"cogs\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Cost of Goods Sold\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"cogs_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Cost of Goods Sold Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_direct_expenses\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Total Direct Expense\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_direct_expenses_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Total Direct Expense Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"gross_profit\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Gross Profit/Loss\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"gross_profit_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Gross Profit/Loss Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"depreciation\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Depreciation\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"depreciation_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Depreciation Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_other_expenses\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Other Expenses\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"other_expenses_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Other Expenses Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_indirect_expenses\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Total Indirect Expenses\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_indirect_expenses_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Total Indirect Expenses Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"net_profit\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Net Profit/Loss\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"net_profit_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Net Profit/Loss Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"expenses_breakup\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Breakup\"\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"direct_expenses_breakup\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Direct Expenses\",\n   \"options\": \"URY P and L Breakup\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"section_break_lught\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"description\": \"Includes in Total Indirect Expense\",\n   \"fieldname\": \"employee_costs_breakup\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Employee Costs\",\n   \"options\": \"URY P and L Breakup\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"section_break_rhkwy\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"indirect_expenses_breakup\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Indirect Expenses\",\n   \"options\": \"URY P and L Breakup\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"cogs_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Cost Of Goods\"\n  },\n  {\n   \"fieldname\": \"cost_of_goods\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Cost Of Goods Sold (Breakup)\",\n   \"options\": \"URY Cost Of Goods\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"amended_from\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Amended From\",\n   \"no_copy\": 1,\n   \"options\": \"URY Daily P and L\",\n   \"print_hide\": 1,\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"section_break_6mqsm\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"fieldname\": \"remarks\",\n   \"fieldtype\": \"Long Text\",\n   \"label\": \"Remarks\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"section_break_dv4iu\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"fieldname\": \"details_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Details\"\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_employee_costs\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Employee Cost\",\n   \"precision\": \"2\",\n   \"read_only\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.docstatus==1\",\n   \"fieldname\": \"total_employee_costs_percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Employee Cost Percent\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"profit_loss_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Profit / Loss\"\n  },\n  {\n   \"fieldname\": \"column_break_bqo3d\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"description\": \"Enter Units Consumed\",\n   \"fieldname\": \"materials_consumed\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Materials Consumed\",\n   \"options\": \"URY P and L Materials\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"is_submittable\": 1,\n \"links\": [],\n \"modified\": \"2024-01-09 14:15:52.566841\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Daily P and L\",\n \"naming_rule\": \"Expression (old style)\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"cancel\": 1,\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"share\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  }\n ],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_daily_p_and_l/ury_daily_p_and_l.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\nimport frappe\nfrom frappe.model.document import Document\nimport json\nimport calendar\nfrom datetime import datetime\n\ndef inner_bom_process(buying_price_list, bom):\n    unset_bom_items = []\n    bom_buying_price = 0\n\n    for bom_item in bom.items:\n        bom_item_qty = bom_item.qty\n        bom_item_name = bom_item.item_name\n        boms = frappe.db.get_all(\"BOM\", fields=(\"*\"), filters={'item': bom_item.item_code, 'is_active': 1, 'is_default': 1, 'docstatus': 1})\n        \n        if len(boms) > 0:\n            inner_bom = frappe.get_doc(\"BOM\", boms[0].name)\n            inner_bom_data = inner_inner_bom_process(buying_price_list, inner_bom)\n            inner_bom_buying_price = inner_bom_data['bom_buying_price']\n            inner_unset_bom_items = inner_bom_data['unset_bom_items']\n            bom_buying_price += float(inner_bom_buying_price) * bom_item_qty\n\n            for item in inner_unset_bom_items:\n                if item not in unset_bom_items:\n                    unset_bom_items.append(item)\n        else:\n            bom_items_price = frappe.db.get_all(\"Item Price\", fields=['name', 'price_list_rate'], filters={'price_list': buying_price_list, 'item_code': bom_item.item_code})\n\n            if len(bom_items_price) == 0:\n                if bom_item_name not in unset_bom_items:\n                    unset_bom_items.append(bom_item_name)\n            else:\n                bom_buying_price += float(bom_items_price[0].price_list_rate) * bom_item_qty\n\n    bom_buying_price = bom_buying_price / bom.quantity\n    return {\"bom_buying_price\": bom_buying_price, \"unset_bom_items\": unset_bom_items}\n\n\ndef inner_inner_bom_process(buying_price_list, bom):\n    unset_bom_items = []\n    bom_buying_price = 0\n\n    for bom_item in bom.items:\n        bom_item_qty = bom_item.qty\n        bom_item_name = bom_item.item_name\n        bom_items_price = frappe.db.get_all(\"Item Price\", fields=['name', 'price_list_rate'], filters={'price_list': buying_price_list, 'item_code': bom_item.item_code})\n        if len(bom_items_price) == 0:\n            if bom_item_name not in unset_bom_items:\n                unset_bom_items.append(bom_item_name)\n        else:\n            bom_buying_price += float(bom_items_price[0].price_list_rate) * bom_item_qty\n\n    bom_buying_price = bom_buying_price / bom.quantity\n    return {\"bom_buying_price\": bom_buying_price, \"unset_bom_items\": unset_bom_items}\n\n\nclass URYDailyPandL(Document):\n\tdef cogs_sold(self):\n\t\treport_settings = frappe.get_doc(\"URY Report Settings\",self.branch)\n\t\tself.cost_of_goods = []\n\t\tcogs = 0\n\t\telectricity_reading = self.electricity_closing - self.electricity_opening\n\t\tif electricity_reading <= 0:\n\t\t\tfrappe.throw(\"Invalid Electricity Reading\")\n\n\t\t# Append materials consumed\n\t\tfor expense in self.materials_consumed:\n\t\t\tif expense.units_consumed <= 0:\n\t\t\t\tfrappe.throw(\"Invavlid Units Consumed for \"+expense.material)\n\t\tnon_pb_item_sales = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\tc.item_group AS \"Item Group\",\n\t\t\t\tc.item_code AS \"Item Code\",\n\t\t\t\tc.item_name AS \"Item Name\",\n\t\t\t\tSUM(b.qty) AS \"Qty\"\n\t\t\tFROM `tabPOS Invoice` a\n\t\t\tINNER JOIN `tabPOS Invoice Item` b ON a.name = b.parent\n\t\t\tLEFT JOIN `tabItem` c ON c.item_code = b.item_code\n\t\t\tLEFT JOIN `tabProduct Bundle` d ON d.new_item_code = b.item_code\n\t\t\tLEFT JOIN `tabBOM` e ON (\n\t\t\t\te.item = b.item_code\n\t\t\t\tAND e.is_active = 1\n\t\t\t\tAND e.is_default = 1\n\t\t\t\tAND e.docstatus = 1\n\t\t\t)\n\t\t\tLEFT JOIN `tabURY Report Settings` rs ON (\n\t\t\t\trs.`branch` = %(branch)s\n\t\t\t)\n\t\t\tWHERE \n\t\t\t\ta.`branch` = %(branch)s\n\t\t\t\tAND a.`status` IN (\"Consolidated\", \"Paid\") \n\t\t\t\tAND a.`docstatus` = 1\n\t\t\t\tAND\n\t\t\t\t(\n\t\t\t\t\t((rs.`hours` IS NULL OR rs.`hours` = 0) AND a.`posting_date` = %(date)s)\n\t\t\t\t\tOR (rs.`hours` > 0 AND TIMESTAMP(a.`posting_date`, a.`posting_time`) <= TIMESTAMP(DATE_ADD(%(date)s, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(a.`posting_date`, a.`posting_time`) >= TIMESTAMP(%(date)s, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\n\t\t\t\t\tOR (rs.`branch` IS NULL AND a.`posting_date` = %(date)s)\n\t\t\t\t)\n\t\t\t\tAND d.new_item_code IS NULL\n\t\t\t\tAND e.item IS NULL\n\t\t\tGROUP BY \n\t\t\t\tc.item_name\n\t\t\tORDER BY \n\t\t\t\tc.item_group ASC, b.item_name ASC\n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\n\t\tbom_item_sales = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\tc.item_group AS \"Item Group\",\n\t\t\t\tc.item_code AS \"Item Code\",\n\t\t\t\tc.item_name AS \"Item Name\",\n\t\t\t\tSUM(b.qty) AS \"Qty\"\n\t\t\tFROM `tabPOS Invoice` a\n\t\t\tINNER JOIN `tabPOS Invoice Item` b ON a.name = b.parent\n\t\t\tLEFT JOIN `tabItem` c ON c.item_code = b.item_code\n\t\t\tLEFT JOIN `tabProduct Bundle` d ON d.new_item_code = b.item_code\n\t\t\tLEFT JOIN `tabBOM` e ON (\n\t\t\t\te.item = b.item_code\n\t\t\t\tAND e.is_active = 1\n\t\t\t\tAND e.is_default = 1\n\t\t\t\tAND e.docstatus = 1\n\t\t\t)\n\t\t\tLEFT JOIN `tabURY Report Settings` rs ON (\n\t\t\t\trs.`branch` = %(branch)s\n\t\t\t)\n\t\t\tWHERE \n\t\t\t\ta.`branch` = %(branch)s\n\t\t\t\tAND a.`status` IN (\"Consolidated\", \"Paid\") \n\t\t\t\tAND a.`docstatus` = 1\n\t\t\t\tAND\n\t\t\t\t(\n\t\t\t\t\t((rs.`hours` IS NULL OR rs.`hours` = 0) AND a.`posting_date` = %(date)s)\n\t\t\t\t\tOR (rs.`hours` > 0 AND TIMESTAMP(a.`posting_date`, a.`posting_time`) <= TIMESTAMP(DATE_ADD(%(date)s, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(a.`posting_date`, a.`posting_time`) >= TIMESTAMP(%(date)s, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\n\t\t\t\t\tOR (rs.`branch` IS NULL AND a.`posting_date` = %(date)s)\n\t\t\t\t)\n\t\t\t\tAND d.new_item_code IS NULL\n\t\t\t\tAND e.item IS NOT NULL\n\t\t\tGROUP BY \n\t\t\t\tc.item_name\n\t\t\tORDER BY \n\t\t\t\tc.item_group ASC, b.item_name ASC\n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\n\t\tpb_item_sales = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\tc.item_group AS \"Item Group\",\n\t\t\t\tc.item_code AS \"Item Code\",\n\t\t\t\tc.item_name AS \"Item Name\",\n\t\t\t\tSUM(b.qty) AS \"Qty\"\n\t\t\tFROM `tabPOS Invoice` a\n\t\t\tINNER JOIN `tabPOS Invoice Item` b ON a.name = b.parent\n\t\t\tLEFT JOIN `tabItem` c ON c.item_code = b.item_code\n\t\t\tLEFT JOIN `tabProduct Bundle` d ON d.new_item_code = b.item_code\n\t\t\tLEFT JOIN `tabURY Report Settings` rs ON (\n\t\t\t\trs.`branch` = %(branch)s\n\t\t\t)\n\t\t\tWHERE \n\t\t\t\ta.`branch` = %(branch)s\n\t\t\t\tAND a.`status` IN (\"Consolidated\", \"Paid\") \n\t\t\t\tAND a.`docstatus` = 1\n\t\t\t\tAND\n\t\t\t\t(\n\t\t\t\t\t((rs.`hours` IS NULL OR rs.`hours` = 0) AND a.`posting_date` = %(date)s)\n\t\t\t\t\tOR (rs.`hours` > 0 AND TIMESTAMP(a.`posting_date`, a.`posting_time`) <= TIMESTAMP(DATE_ADD(%(date)s, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(a.`posting_date`, a.`posting_time`) >= TIMESTAMP(%(date)s, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\n\t\t\t\t\tOR (rs.`branch` IS NULL AND a.`posting_date` = %(date)s)\n\t\t\t\t)\n\t\t\t\tAND d.new_item_code IS NOT NULL\n\t\t\tGROUP BY \n\t\t\t\tc.item_name\n\t\t\tORDER BY \n\t\t\t\tc.item_group ASC, b.item_name ASC\n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\t\t\n\t\tunset_item_prices = []\n\t\tfor item in non_pb_item_sales:\n\t\t\titems_price = frappe.db.get_all(\"Item Price\",fields = ['name','price_list_rate'],filters = {'price_list':report_settings.buying_price_list,'item_code':item['Item Code']})\n\t\t\titem_name = item['Item Name']\n\t\t\tif len(items_price) == 0:\n\t\t\t\tunset_item_prices.append(item_name)\n\t\t\telse:\n\t\t\t\tqty = int(item['Qty'])\n\t\t\t\tself.append(\"cost_of_goods\" ,{\n\t\t\t\t\t\"item_code\":item['Item Code'],\n\t\t\t\t\t\"item_name\":item['Item Name'],\n\t\t\t\t\t\"item_group\":item['Item Group'],\n\t\t\t\t\t\"qty\":qty,\n\t\t\t\t\t\"buying_price\":items_price[0].price_list_rate,\n\t\t\t\t\t\"amount\":items_price[0].price_list_rate * qty\n\t\t\t\t})\n\t\t\t\tcogs = cogs + items_price[0].price_list_rate * qty\n\t\t\n\t\tunset_bom_item_prices = []\n\t\tfor item in bom_item_sales:\n\t\t\tbuying_price = 0\n\t\t\tbuying_price_list = report_settings.buying_price_list\n\t\t\tboms = frappe.db.get_all(\"BOM\",fields = (\"*\"),filters = {'item':item['Item Code'],'is_active':1,'is_default':1,'docstatus':1})\n\t\t\tbom = frappe.get_doc(\"BOM\",boms[0].name)\n\t\t\tbom_data = inner_bom_process(buying_price_list,bom)\n\t\t\tbom_buying_price = bom_data['bom_buying_price']\n\t\t\tunset_bom_items = bom_data['unset_bom_items']\n\t\t\tbuying_price += float(bom_buying_price)\n\t\t\tfor unset_item in unset_bom_items:\n\t\t\t\tif unset_item not in unset_bom_item_prices:\n\t\t\t\t\tunset_bom_item_prices.append(unset_item)\n\t\t\tif buying_price > 0:\n\t\t\t\tqty = float(item['Qty'])\n\t\t\t\tself.append(\"cost_of_goods\" ,{\n\t\t\t\t\t\"item_code\":item['Item Code'],\n\t\t\t\t\t\"item_name\":item['Item Name'],\n\t\t\t\t\t\"item_group\":item['Item Group'],\n\t\t\t\t\t\"qty\":qty,\n\t\t\t\t\t\"buying_price\":buying_price,\n\t\t\t\t\t\"amount\":buying_price * qty\n\t\t\t\t})\n\t\t\t\tcogs = cogs + buying_price * qty\n\n\t\tunset_pb_item_prices = []\n\t\tfor item in pb_item_sales:\n\t\t\tpb_items = frappe.db.get_all(\"Product Bundle\",fields = (\"*\"),filters = {'new_item_code':item['Item Code']})\n\t\t\tpb = frappe.get_doc(\"Product Bundle\",pb_items[0].name)\n\t\t\tbuying_price = 0\n\t\t\tfor pb_item in pb.items:\n\t\t\t\titem_qty = pb_item.qty\n\t\t\t\tboms = frappe.db.get_all(\"BOM\",fields = (\"*\"),filters = {'item':pb_item.item_code,'is_active':1,'is_default':1,'docstatus':1})\n\t\t\t\tif len(boms) > 0:\n\t\t\t\t\tbuying_price_list = report_settings.buying_price_list\n\t\t\t\t\tbom = frappe.get_doc(\"BOM\",boms[0].name)\n\t\t\t\t\tbom_data = inner_bom_process(buying_price_list,bom)\n\t\t\t\t\tbom_buying_price = bom_data['bom_buying_price']\n\t\t\t\t\tunset_bom_items = bom_data['unset_bom_items']\n\t\t\t\t\tbuying_price += float(bom_buying_price)*item_qty\n\t\t\t\t\tfor unset_item in unset_bom_items:\n\t\t\t\t\t\tif unset_item not in unset_bom_item_prices:\n\t\t\t\t\t\t\tunset_bom_item_prices.append(unset_item)\n\t\t\t\telse:\n\t\t\t\t\tsub_item = frappe.get_doc(\"Item\",pb_item.item_code)\n\t\t\t\t\titem_name = sub_item.item_name\n\t\t\t\t\titems_price = frappe.db.get_all(\"Item Price\",fields = ['name','price_list_rate'],filters = {'price_list':report_settings.buying_price_list,'item_code':pb_item.item_code})\n\t\t\t\t\tif len(items_price) == 0:\n\t\t\t\t\t\tif item_name not in unset_pb_item_prices:\n\t\t\t\t\t\t\tunset_pb_item_prices.append(item_name)\n\t\t\t\t\telse:\n\t\t\t\t\t\tbuying_price += float(items_price[0].price_list_rate)*item_qty\n\t\t\t\n\t\t\tif buying_price > 0:\n\t\t\t\tqty = float(item['Qty'])\n\t\t\t\tself.append(\"cost_of_goods\" ,{\n\t\t\t\t\t\"item_code\":item['Item Code'],\n\t\t\t\t\t\"item_name\":item['Item Name'],\n\t\t\t\t\t\"item_group\":item['Item Group'],\n\t\t\t\t\t\"qty\":qty,\n\t\t\t\t\t\"buying_price\":buying_price,\n\t\t\t\t\t\"amount\":buying_price * qty\n\t\t\t\t})\n\t\t\t\tcogs = cogs + buying_price * qty\n\t\tself.cogs = cogs\n\t\t\n\t\tunset_prices = [\n\t\t\t(\"ITEMS\", unset_item_prices),\n\t\t\t(\"BUNDLE SUB ITEMS\", unset_pb_item_prices),\n\t\t\t(\"BOM SUB ITEMS\", unset_bom_item_prices)\n\t\t]\n\t\tremarks = \"BUYING PRICE NOT SET<br><br>\" + \"<br><br>\".join(f\"{label}:-<br>{items}\" for label, items in unset_prices if items)\n\t\tif any(items for label, items in unset_prices):\n\t\t\tremarks += \"<br><br>Update the item prices and then submit the document again to ensure accurate Cost of Goods\"\n\t\t\tself.remarks = remarks\n\t\telse:\n\t\t\tself.remarks = \"\"\n\n\t\n\tdef before_save(self):\n\t\tself.cogs_sold()\n\t\tif self.remarks != \"\":\n\t\t\tfrappe.msgprint(title='SET BUYING PRICE',msg=(\"Please review the remarks below for the items. Submitting now will exclude these items from the cost of goods.\"))\n\t\n\tdef before_submit(self):\n\t\tself.cogs_sold()\n\t\t\n\t\treport_settings = frappe.get_doc(\"URY Report Settings\",self.branch)\n\n\t\tself.direct_expenses_breakup = []\n\t\tself.employee_costs_breakup = []\n\t\tself.indirect_expenses_breakup = []\n\n\t\t'''Total Sales Of the Day'''\n\t\tgross_sales = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\t%(date)s AS \"Date\",\n\t\t\t\tCOUNT(b.`name`) AS \"Total Invoices\",\n\t\t\t\tROUND(SUM(b.`net_total`),2) AS \"Item Total\",\n\t\t\t\tROUND(SUM(b.`grand_total` - b.`net_total`),2) AS \"Tax\",\n\t\t\t\tROUND(SUM(b.`grand_total`),2) AS \"Grand Total\",\n\t\t\t\tROUND(SUM(b.`grand_total` - b.`rounded_total`),2) AS \"Round Off\",\n\t\t\t\tROUND(SUM(b.`rounded_total`),2) AS \"Rounded Total\",\n\t\t\t\tROUND(SUM(b.`rounded_total` - b.`paid_amount` + b.`change_amount`),2) AS \"Cash Discounts\"\n\t\t\tFROM `tabPOS Invoice` b\n\t\t\tLEFT JOIN `tabURY Report Settings` rs ON (\n\t\t\t\trs.`branch` = %(branch)s\n\t\t\t)\n\t\t\tWHERE \n\t\t\t\tb.`branch` = %(branch)s\n\t\t\t\tAND b.`status` IN (\"Consolidated\", \"Paid\") \n\t\t\t\tAND b.`docstatus` = 1\n\t\t\t\tAND\n\t\t\t\t(\n\t\t\t\t\t((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = %(date)s)\n\t\t\t\t\tOR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(%(date)s, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(%(date)s, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\n\t\t\t\t\tOR (rs.`branch` IS NULL AND b.`posting_date` = %(date)s)\n\t\t\t\t)\n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\n\n\t\t# GROSS AND NET SALES\n\t\tself.gross_sales = 0.0\n\t\tgross_sales_all =  gross_sales[0]\n\t\tif gross_sales_all['Total Invoices'] == 0:\n\t\t\tself.gross_sales = 0.0\n\t\t\tgross_sales = gross_sales[0]\n\t\t\tgross_sales[\"Round Off\"] = gross_sales[\"Cash Discounts\"] = gross_sales[\"Tax\"] = 0.0\n\t\telse:\n\t\t\tgross_sales = gross_sales[0]\n\t\t\tself.gross_sales = gross_sales[\"Grand Total\"]\n\n\t\tself.cash_discount_round_off = round((gross_sales[\"Round Off\"] + gross_sales[\"Cash Discounts\"]),2)\n\t\tself.tax = gross_sales[\"Tax\"]\n\n\t\tself.net_sales = self.gross_sales - self.cash_discount_round_off - self.tax\n\n\t\tif self.net_sales == 0.0:\n\t\t\tself.gross_sales_percent = self.cash_discount_round_off_percent = self.tax_percent = self.cogs_percent = self.total_direct_expenses_percent = self.gross_profit_percent = self.total_employee_costs_percent = self.other_expenses_percent = self.depreciation_percent = self.total_indirect_expenses_percent = self.net_profit_percent = 0.0\n\t\telse:\n\t\t\tself.gross_sales_percent = round(((self.gross_sales / self.net_sales) * 100),2)\n\t\t\tself.cash_discount_round_off_percent = round(((self.cash_discount_round_off / self.net_sales) * 100),2)\n\t\t\tself.tax_percent = round(((self.tax / self.net_sales) * 100),2)\n\n\t\t\tself.cogs_percent = round(((self.cogs / self.net_sales) * 100),2)\n\n\t\tself.net_sales_percent = 100.0\n\n\n\t\t# DIRECT EXPENSES\n\t\tself.total_direct_expenses = 0\n\t\telectricity_reading = self.electricity_closing - self.electricity_opening\n\n\t\t# Append materials consumed\n\t\tfor expense in self.materials_consumed:\n\t\t\tself.append(\"direct_expenses_breakup\", {\"breakup\": expense.material, \"amount\": expense.amount})\n\t\t\tself.total_direct_expenses += expense.amount\n\n\t\t# Append other fixed expenses\n\t\tfor expense in report_settings.direct_fixed_expenses:\n\t\t\tself.append(\"direct_expenses_breakup\", {\"breakup\": expense.expense, \"amount\": expense.amount})\n\t\t\tself.total_direct_expenses += expense.amount\n\n\n\t\t# GROSS PROFIT\n\t\tself.gross_profit = self.net_sales - self.total_direct_expenses - self.cogs\n\t\t\n\t\tif self.net_sales != 0.0:\t\t\n\t\t\tself.total_direct_expenses_percent = round(((self.total_direct_expenses / self.net_sales) * 100),2)\n\t\t\tself.gross_profit_percent = round(((self.gross_profit / self.net_sales) * 100),2)\n\n\n\t\t'''INDIRECT EXPENSES'''\n\n\t\tself.total_indirect_expenses = 0\n\n\n\t\t#EMPLOYEE COSTS\n\t\tsalary_cost_gross = 0\n\t\tself.total_employee_costs = 0\n\n\t\tattendance_count = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\t%(date)s AS \"Date\",\n\t\t\t\tCOUNT(b.`name`) AS \"Total Attendance\"\n\t\t\tFROM `tabAttendance` b\n\t\t\tLEFT JOIN `tabEmployee` c ON \n\t\t\tc.name = b.employee\n\t\t\tWHERE \n\t\t\t\tb.`attendance_date` = %(date)s\n\t\t\t\tAND c.`branch` = %(branch)s\n\t\t\t\tAND b.`docstatus` = 1\n\t\t\t\tAND b.`status` IN (\"Present\", \"Half Day\")\n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)    \n\n\t\tattendance_count =  attendance_count[0]\n\n\t\tif attendance_count['Total Attendance'] == 0:\n\t\t\tfrappe.throw(title='No Attendance !',msg=(\"Attendance not marked\"))\n\n\t\tns_employee_attendance_list = frappe.db.sql(''' \n\t\t\tSELECT\n\t\t\t\tb.`employee_name` AS \"Name\"\n\t\t\tFROM `tabAttendance` b\n\t\t\tLEFT JOIN `tabEmployee` c ON (\n\t\t\t\tc.name = b.employee\n\t\t\t\tAND (\n\t\t\t\t\t(c.`payment_amount` IS NULL OR c.`payment_amount` = 0.0 ) \n\t\t\t\t\tOR \n\t\t\t\t\t(c.`payment_type` IS NULL OR c.`payment_type` = \"\")\n\t\t\t\t)\n\t\t\t)\n\t\t\tWHERE \n\t\t\t\tb.`attendance_date` = %(date)s\n\t\t\t\tAND c.`branch` = %(branch)s\n\t\t\tGROUP BY\n\t\t\t\tb.`employee_name`                \n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\n\t\tif len(ns_employee_attendance_list) > 0:\n\t\t\tns_employee_attendance_list = json.dumps(ns_employee_attendance_list)\n\t\t\tfrappe.throw(title='Set Payment Type/Amount',msg=(\"Employees:  {0}\").format(ns_employee_attendance_list))\n\n\t\temployee_attendance_dw_list = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\t%(date)s AS \"Date\",\n\t\t\t\tb.`employee` AS \"Employee\",\n\t\t\t\tb.`status` AS \"Status\",\n\t\t\t\tc.`payment_amount` AS \"Salary\"\n\t\t\tFROM `tabAttendance` b\n\t\t\tLEFT JOIN `tabEmployee` c ON c.name = b.employee\n\t\t\tWHERE \n\t\t\t\tb.`attendance_date` = %(date)s\n\t\t\t\tAND c.`branch` = %(branch)s\n\t\t\t\tAND c.`payment_type` = \"Daily Wage\"                        \n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\n\t\tfor attendance in employee_attendance_dw_list:\n\t\t\tif attendance[\"Status\"] == \"Half Day\":\n\t\t\t\tsalary_cost_gross = round((salary_cost_gross + 0.5 * attendance[\"Salary\"]),2)\n\t\t\tif attendance[\"Status\"] == \"Present\":\n\t\t\t\tsalary_cost_gross = round((salary_cost_gross + attendance[\"Salary\"]),2)\n\n\t\tdate_str =  self.date\n\t\tdate_obj = datetime.strptime(date_str, '%Y-%m-%d')\n\t\tyear = date_obj.year\n\t\tmonth_number = date_obj.month\n\t\tdays = calendar.monthrange(year, month_number)[1]\n\n\t\temployee_attendance_sl_list = frappe.db.sql('''\n\t\t\tSELECT\n\t\t\t\t%(date)s AS \"Date\",\n\t\t\t\tb.`name` AS \"Employee\",\n\t\t\t\tb.`payment_amount` AS \"Salary\"\n\t\t\tFROM `tabEmployee` b\n\t\t\tWHERE \n\t\t\t\tb.`branch` = %(branch)s\n\t\t\t\tAND b.`payment_type` = \"Salary\"                        \n\t\t''', {\"branch\": self.branch, \"date\": self.date}, as_dict=True)\n\n\t\tfor attendance in employee_attendance_sl_list:\n\t\t\tsalary_cost_gross = round((salary_cost_gross + attendance[\"Salary\"]/days),2)\n\n\t\tif self.net_sales != 0.0:\n\t\t\tsalary_cost_gross_percent = round(((salary_cost_gross / self.net_sales) * 100),3)\n\t\telse:\n\t\t\tsalary_cost_gross_percent = 0.0\n\t\tself.append(\"employee_costs_breakup\", {\"breakup\": \"Salary Cost Gross\", \"amount\": salary_cost_gross,\"percent\":salary_cost_gross_percent})\n\t\tself.total_employee_costs += salary_cost_gross\n\n\t\tfor expense in report_settings.employee_costs:\n\t\t\tif self.net_sales != 0.0:\n\t\t\t\texpense_percent = round(((expense.amount / self.net_sales) * 100),3)\n\t\t\telse:\n\t\t\t\texpense_percent = 0.0\n\t\t\tself.append(\"employee_costs_breakup\", {\"breakup\": expense.expense, \"amount\": expense.amount,\"percent\":expense_percent})\n\t\t\tself.total_employee_costs += expense.amount\n\t\tif self.net_sales != 0.0:\n\t\t\tself.total_employee_costs_percent = round(((self.total_employee_costs / self.net_sales) * 100),3)\n\t\tself.total_indirect_expenses += self.total_employee_costs\n\n\t\t#INDIRECT EXPENSES\n\t\t\n\t\t# Calculate and append Electricity\n\t\telectricity_charges = electricity_reading * report_settings.electricity_charges\n\t\tif self.net_sales != 0.0:\n\t\t\telectricity_percent = round(((electricity_charges / self.net_sales) * 100),3)\n\t\telse:\n\t\t\telectricity_percent = 0.0\n\t\tself.append(\"indirect_expenses_breakup\", {\"breakup\": \"Electricity\", \"amount\": electricity_charges,\"percent\":electricity_percent})\n\t\tself.total_indirect_expenses += electricity_charges\n\n\t\t# Append indirect fixed expenses\n\t\tfor expense in report_settings.indirect_fixed_expenses:\n\t\t\tif self.net_sales != 0.0:\n\t\t\t\texpense_percent = round(((expense.amount / self.net_sales) * 100),3)\n\t\t\telse:\n\t\t\t\texpense_percent = 0.0\n\t\t\tself.append(\"indirect_expenses_breakup\", {\"breakup\": expense.expense, \"amount\": expense.amount,\"percent\":expense_percent})\n\t\t\tself.total_indirect_expenses += expense.amount\n\n\t\t# Append indirect monthly fixed expenses\n\t\tfor expense in report_settings.monthly_fixed_expenses:\n\t\t\texpense_amount = round((expense.amount/days),2)\n\t\t\tif self.net_sales != 0.0:\n\t\t\t\texpense_percent = round(((expense_amount / self.net_sales) * 100),3)\n\t\t\telse:\n\t\t\t\texpense_percent = 0.0\n\t\t\tself.append(\"indirect_expenses_breakup\", {\"breakup\": expense.expense, \"amount\": expense_amount,\"percent\":expense_percent})\n\t\t\tself.total_indirect_expenses += expense_amount\n\n\t\t# Calculate and append percentage expenses\n\t\tfor expense in report_settings.percentage_expenses:\n\t\t\tif expense.percentage_type in [\"Gross Sales\", \"Net Sales\"]:\n\t\t\t\tbase_amount = self.gross_sales if expense.percentage_type == \"Gross Sales\" else self.net_sales\n\t\t\t\tamount = round((expense.percent * base_amount) / 100, 2)\n\t\t\t\tif self.net_sales != 0.0:\n\t\t\t\t\texpense_percent = round(((amount / self.net_sales) * 100),3)\n\t\t\t\telse:\n\t\t\t\t\texpense_percent = 0.0\n\t\t\t\tself.append(\"indirect_expenses_breakup\", {\"breakup\": expense.expense, \"amount\": amount ,\"percent\":expense_percent})\n\t\t\t\tself.total_indirect_expenses += amount\n\n\t\t# OTHER EXPENSES\n\t\tself.total_other_expenses = 0\n\t\tfor expense in self.other_expenses:\n\t\t\tself.total_indirect_expenses += expense.amount\n\t\t\tself.total_other_expenses += expense.amount\n\n\t\tself.depreciation = report_settings.depreciation\n\t\t\n\t\tself.total_indirect_expenses = self.total_indirect_expenses + self.depreciation\n\n\t\t# NET PROFIT\n\t\tself.net_profit = self.gross_profit - self.total_indirect_expenses\n\n\t\tif self.net_sales != 0.0:\n\t\t\tself.other_expenses_percent = round(((self.total_other_expenses / self.net_sales) * 100),3)\n\t\t\tself.depreciation_percent = round(((self.depreciation / self.net_sales) * 100),3)\n\t\t\tself.total_indirect_expenses_percent = round(((self.total_indirect_expenses / self.net_sales) * 100),2)\n\t\t\tself.net_profit_percent = round(((self.net_profit / self.net_sales) * 100),2)\n\t\n\t@frappe.whitelist()\n\tdef get_proft_loss_details(self):\n\t\treturn frappe.render_template(\n\t\t\t\"ury/doctype/ury_daily_p_and_l/profit_loss_details.html\",\n\t\t\t{\"data\": self, \"currency\": \"INR\"},\n\t\t)"
  },
  {
    "path": "ury/ury/doctype/ury_fixed_expenses/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_fixed_expenses/ury_fixed_expenses.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-10-04 14:47:45.370491\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"expense\",\n  \"amount\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"expense\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Expense\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"amount\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Amount\",\n   \"precision\": \"2\",\n   \"reqd\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-10-05 16:04:21.456296\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Fixed Expenses\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_fixed_expenses/ury_fixed_expenses.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYFixedExpenses(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_kot/test_ury_kot.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYKOT(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot/ury_kot.js",
    "content": "// Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY KOT', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot/ury_kot.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"autoname\": \"naming_series:\",\n \"creation\": \"2023-09-27 12:19:48.275531\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"invoice\",\n  \"restaurant_table\",\n  \"customer_name\",\n  \"original_kot\",\n  \"column_break_phntx\",\n  \"date\",\n  \"time\",\n  \"type\",\n  \"section_break_l97s0\",\n  \"order_status\",\n  \"production\",\n  \"section_break_yxcxo\",\n  \"start_time_prep\",\n  \"production_time\",\n  \"column_break_9rksu\",\n  \"start_time_serv\",\n  \"section_break_ubzfc\",\n  \"kot_items\",\n  \"section_break_zpwun\",\n  \"naming_series\",\n  \"pos_profile\",\n  \"comments\",\n  \"branch\",\n  \"verified\",\n  \"order_no\",\n  \"aggregator_id\",\n  \"is_aggregator\",\n  \"verified_by\",\n  \"customer_group\",\n  \"table_takeaway\",\n  \"user\",\n  \"amended_from\"\n ],\n \"fields\": [\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"invoice\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Invoice\",\n   \"read_only\": 1\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"restaurant_table\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Restaurant Table\",\n   \"options\": \"URY Table\"\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"customer_name\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Customer Name\",\n   \"options\": \"Customer\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"original_kot\",\n   \"fieldtype\": \"Small Text\",\n   \"label\": \"Original KOT\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"column_break_phntx\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"default\": \"Today\",\n   \"fieldname\": \"date\",\n   \"fieldtype\": \"Date\",\n   \"in_list_view\": 1,\n   \"label\": \"Date\",\n   \"reqd\": 1\n  },\n  {\n   \"default\": \"Now\",\n   \"fieldname\": \"time\",\n   \"fieldtype\": \"Time\",\n   \"label\": \"Time\"\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"default\": \"New Order\",\n   \"fieldname\": \"type\",\n   \"fieldtype\": \"Select\",\n   \"label\": \"Type\",\n   \"options\": \"\\nNew Order\\nOrder Modified\\nCancelled\\nPartially cancelled\\nDuplicate\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"section_break_l97s0\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"default\": \"Ready For Prepare\",\n   \"fieldname\": \"order_status\",\n   \"fieldtype\": \"Data\",\n   \"in_standard_filter\": 1,\n   \"label\": \"Order Status\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"production\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Production\",\n   \"options\": \"URY Production Unit\"\n  },\n  {\n   \"fieldname\": \"section_break_yxcxo\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"default\": \"Now\",\n   \"fieldname\": \"start_time_prep\",\n   \"fieldtype\": \"Time\",\n   \"label\": \"Start Time For Preparation\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"column_break_9rksu\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"start_time_serv\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Served Time\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"section_break_ubzfc\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"kot_items\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"KOT Items\",\n   \"options\": \"URY KOT Items\"\n  },\n  {\n   \"fieldname\": \"section_break_zpwun\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"fieldname\": \"naming_series\",\n   \"fieldtype\": \"Data\",\n   \"hidden\": 1,\n   \"label\": \"naming_series\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"pos_profile\",\n   \"fieldtype\": \"Link\",\n   \"hidden\": 1,\n   \"label\": \"POS Profile\",\n   \"options\": \"POS Profile\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"comments\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Comments\",\n   \"read_only\": 1\n  },\n  {\n   \"fetch_from\": \"pos_profile.branch\",\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"read_only\": 1\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"default\": \"0\",\n   \"fieldname\": \"verified\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Verified\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"order_no\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Order No\",\n   \"read_only\": 1\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"verified_by\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"verified_by\",\n   \"options\": \"User\",\n   \"read_only\": 1\n  },\n  {\n   \"fetch_from\": \"customer_name.customer_group\",\n   \"fieldname\": \"customer_group\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Customer group\"\n  },\n  {\n   \"default\": \"0\",\n   \"fetch_from\": \"restaurant_table.is_take_away\",\n   \"fieldname\": \"table_takeaway\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Table Takeaway\"\n  },\n  {\n   \"fieldname\": \"user\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"user\"\n  },\n  {\n   \"fieldname\": \"aggregator_id\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Aggregator ID\",\n   \"read_only\": 1\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"is_aggregator\",\n   \"fieldtype\": \"Check\",\n   \"hidden\": 1,\n   \"label\": \"Is Aggregator\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"amended_from\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Amended From\",\n   \"no_copy\": 1,\n   \"options\": \"URY KOT\",\n   \"print_hide\": 1,\n   \"read_only\": 1\n  },\n  {\n   \"allow_on_submit\": 1,\n   \"fieldname\": \"production_time\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Total Production Time\",\n   \"read_only\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"is_submittable\": 1,\n \"links\": [],\n \"modified\": \"2024-08-26 12:28:58.664903\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY KOT\",\n \"naming_rule\": \"By \\\"Naming Series\\\" field\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"cancel\": 1,\n   \"create\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1,\n   \"share\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  },\n  {\n   \"cancel\": 1,\n   \"create\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"share\": 1,\n   \"submit\": 1,\n   \"write\": 1\n  }\n ],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_kot/ury_kot.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\nimport frappe\nimport requests\nimport json\nfrom frappe.utils.print_format import print_by_server\nfrom frappe.model.document import Document\n\n\nclass URYKOT(Document):\n    def on_submit(self):\n        self.multi_print_kot()\n        self.kotDisplayRealtime()\n\n    def before_submit(self):\n        self.userSetting()\n\n    # Function for printing multiple KOTs.\n    def multi_print_kot(self):\n        # Function for printing a KOT on a specified printer using a print format.\n        def print_kot(printer, kot_print_format):\n            try:\n                # Print KOT using a server function (print_by_server)\n                print_by_server(\"URY KOT\", self.name, printer, kot_print_format)\n            except:\n                pass\n\n        \n        pos_kot_printers = frappe.db.get_all(\n            \"URY Printer Settings\",\n            fields=[\"printer\", \"custom_kot_print_format\",\"custom_kot_print\"], \n            filters={\"parent\": self.pos_profile, \"custom_kot_print\": 1,\"parenttype\":\"POS Profile\"},\n            order_by=\"idx\"\n        )\n    \n        pos_print_flag = True\n        if self.production:\n            production_unit_printers = frappe.get_all(\n                \"URY Printer Settings\",\n                fields=[\"printer\", \"custom_kot_print_format\",\"custom_kot_print\",\"custom_block_takeaway_kot\"], \n                filters={\"parent\": self.production, \"custom_kot_print\": 1,\"parenttype\":\"URY Production Unit\"},\n                order_by=\"idx\"\n            )\n\n            # If production unit printer is specified, print KOT in production printer\n            if production_unit_printers:\n                for printer in production_unit_printers:\n                    pos_print_flag = False\n                    if printer.custom_block_takeaway_kot == 1 :\n                        if self.restaurant_table and self.table_takeaway == 0:\n                            print_kot(printer.printer, printer.custom_kot_print_format)\n                    else:\n                        print_kot(printer.printer, printer.custom_kot_print_format)\n\n                # Check if restaurant table is specified and it's not a takeaway order\n                if self.restaurant_table and self.table_takeaway == 0:\n                    room = frappe.db.get_value(\n                        \"URY Table\", self.restaurant_table, \"restaurant_room\"\n                    )\n\n                    room_kot_printers = frappe.get_all(\n                        \"URY Printer Settings\",\n                        fields=[\"printer\", \"custom_kot_print_format\",\"custom_kot_print\"],\n                        filters={\"parent\": room, \"custom_kot_print\": 1,\"parenttype\":\"URY Room\"},\n                        order_by=\"idx\"\n                    )\n                    \n                    # If room printer is specified, print KOT in room\n                    if room_kot_printers:\n                        for printer in room_kot_printers:\n                            pos_print_flag = False\n                            print_kot(printer.printer, printer.custom_kot_print_format)\n\n                    if pos_print_flag == True:\n                        if pos_kot_printers:\n                            for printer in pos_kot_printers:\n                                print_kot(printer.printer, printer.custom_kot_print_format)\n\n                else:\n                    if pos_kot_printers:\n                        for printer in pos_kot_printers:\n                            print_kot(printer.printer, printer.custom_kot_print_format)\n\n\n    # Function for displaying KOT-related information in real-time On KDS(Kitchen Display System)\n    def kotDisplayRealtime(self):\n        currentBranch = self.branch\n        production = self.production\n        kotjson = json.loads(frappe.as_json(self))\n        audio_file = frappe.db.get_value(\n            \"POS Profile\", self.pos_profile, \"custom_kot_alert_sound\"\n        )\n        cache_key = \"{}_{}_last_kot_time\".format(currentBranch, production)\n        time = frappe.cache().get_value(cache_key)\n        kot_channel = \"{}_{}_{}\".format(\"kot_update\", currentBranch, production)\n        frappe.publish_realtime(\n            kot_channel,\n            {\"kot\": kotjson, \"audio_file\": audio_file, \"last_kot_time\": time},\n        )\n        frappe.cache().set_value(cache_key, self.time)\n\n    def userSetting(self):\n        userDoc = frappe.get_doc(\"User\", self.owner)\n        self.user = userDoc.full_name\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot_error_log/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_kot_error_log/test_ury_kot_error_log.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYKOTErrorLog(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot_error_log/ury_kot_error_log.js",
    "content": "// Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY KOT Error Log', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot_error_log/ury_kot_error_log.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-09-27 12:17:01.570160\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"kot\",\n  \"date\",\n  \"time\",\n  \"invoice\",\n  \"invoice_creation_time\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"kot\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"KOT\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"Date\",\n   \"read_only\": 1\n  },\n  {\n   \"default\": \"Now\",\n   \"fieldname\": \"time\",\n   \"fieldtype\": \"Time\",\n   \"label\": \"Time\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"invoice\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Invoice\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"invoice_creation_time\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Invoice Creation Time\",\n   \"read_only\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"links\": [],\n \"modified\": \"2023-09-27 12:17:01.570160\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY KOT Error Log\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"export\": 1,\n   \"read\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"read\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"write\": 1\n  }\n ],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_kot_error_log/ury_kot_error_log.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYKOTErrorLog(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_kot_items/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_kot_items/ury_kot_items.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"autoname\": \"URYKOTITM.#####\",\n \"creation\": \"2023-09-27 12:18:52.086410\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item\",\n  \"item_name\",\n  \"quantity\",\n  \"cancelled_qty\",\n  \"comments\",\n  \"course\",\n  \"serve_priority\",\n  \"indicate_course\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"item\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item\",\n   \"options\": \"Item\"\n  },\n  {\n   \"fetch_from\": \"item.item_name\",\n   \"fieldname\": \"item_name\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Item Name\"\n  },\n  {\n   \"fieldname\": \"quantity\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Quantity\"\n  },\n  {\n   \"fieldname\": \"cancelled_qty\",\n   \"fieldtype\": \"Data\",\n   \"in_preview\": 1,\n   \"label\": \"Cancelled Qty\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"comments\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Comments\"\n  },\n  {\n   \"fieldname\": \"course\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Course\",\n   \"options\": \"URY Menu Course\",\n   \"read_only\": 1\n  },\n  {\n   \"fetch_from\": \"course.custom_serving_priority\",\n   \"fieldname\": \"serve_priority\",\n   \"fieldtype\": \"Int\",\n   \"label\": \"Serve Priority\",\n   \"read_only\": 1\n  },\n  {\n   \"default\": \"0\",\n   \"fetch_from\": \"course.custom_indicate_in_kds\",\n   \"fieldname\": \"indicate_course\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Indicate Course\",\n   \"read_only\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2024-08-28 16:23:35.778511\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY KOT Items\",\n \"naming_rule\": \"Expression (old style)\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_kot_items/ury_kot_items.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYKOTItems(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_materials/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_materials/ury_materials.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-10-05 10:13:18.431268\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"material\",\n  \"cost_per_unit\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"cost_per_unit\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Cost Per Unit\",\n   \"precision\": \"2\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"material\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Material\",\n   \"reqd\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-10-06 11:51:25.022608\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Materials\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_materials/ury_materials.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYMaterials(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_menu/test_ury_menu.py",
    "content": "# Copyright (c) 2023, Tridz Technologies  and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYMenu(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu/ury_menu.js",
    "content": "//  Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n//  For license information, please see license.txt\n\nfrappe.ui.form.on('URY Menu', {\n\tsetup: function (frm) {\n\t\tfrm.add_fetch('item', 'standard_rate', 'rate');\n\t},\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu/ury_menu.json",
    "content": "{\n \"actions\": [],\n \"autoname\": \"prompt\",\n \"creation\": \"2023-09-13 23:42:45.930064\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"document_type\": \"Setup\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"branch\",\n  \"enabled\",\n  \"column_break_3\",\n  \"price_list\",\n  \"items_section\",\n  \"items\"\n ],\n \"fields\": [\n  {\n   \"default\": \"1\",\n   \"fieldname\": \"enabled\",\n   \"fieldtype\": \"Check\",\n   \"in_list_view\": 1,\n   \"label\": \"Enabled\"\n  },\n  {\n   \"fieldname\": \"column_break_3\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"price_list\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Price List (Auto created)\",\n   \"options\": \"Price List\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"items_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Items\"\n  },\n  {\n   \"allow_bulk_edit\": 1,\n   \"fieldname\": \"items\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Items\",\n   \"options\": \"URY Menu Item\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"reqd\": 1\n  }\n ],\n \"links\": [],\n \"modified\": \"2025-07-17 11:30:26.485161\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Menu\",\n \"naming_rule\": \"Set by user\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1\n  },\n  {\n   \"print\": 1,\n   \"read\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"write\": 1\n  }\n ],\n \"quick_entry\": 1,\n \"row_format\": \"Dynamic\",\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": [],\n \"track_changes\": 1\n}"
  },
  {
    "path": "ury/ury/doctype/ury_menu/ury_menu.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\nimport frappe\nfrom frappe.model.document import Document\n\n\nclass URYMenu(Document):\n    def validate(self):\n        for d in self.items:\n            if not d.rate:\n                d.rate = frappe.db.get_value(\"Item\", d.item, \"standard_rate\")\n\n    def on_update(self):\n        \"\"\"Sync Price List\"\"\"\n        self.make_price_list()\n\n    def on_trash(self):\n        \"\"\"clear prices\"\"\"\n        self.clear_item_price()\n\n    def clear_item_price(self, price_list=None):\n        \"\"\"clear all item prices for this menu\"\"\"\n        if not price_list:\n            price_list = self.get_price_list().name\n        frappe.db.sql(\"delete from `tabItem Price` where price_list = %s\", price_list)\n\n    def make_price_list(self):\n        # create price list for menu\n        price_list = self.get_price_list()\n        self.db_set(\"price_list\", price_list.name)\n\n        # delete old items\n        self.clear_item_price(price_list.name)\n\n        for d in self.items:\n            frappe.get_doc(\n                dict(\n                    doctype=\"Item Price\",\n                    price_list=price_list.name,\n                    item_code=d.item,\n                    price_list_rate=d.rate,\n                )\n            ).insert()\n\n    def get_price_list(self):\n        \"\"\"Create price list for menu if missing\"\"\"\n        price_list_name = frappe.db.get_value(\n            \"Price List\", dict(restaurant_menu=self.name)\n        )\n        if price_list_name:\n            price_list = frappe.get_doc(\"Price List\", price_list_name)\n        else:\n            price_list = frappe.new_doc(\"Price List\")\n            price_list.restaurant_menu = self.name\n            price_list.price_list_name = self.name\n\n        price_list.enabled = 1\n        price_list.selling = 1\n        price_list.save()\n\n        return price_list\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu_course/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_menu_course/test_ury_menu_course.py",
    "content": "# Copyright (c) 2024, Tridz Technologies Pvt. Ltd and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYMenuCourse(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu_course/ury_menu_course.js",
    "content": "// Copyright (c) 2024, Tridz Technologies Pvt. Ltd and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY Menu Course', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu_course/ury_menu_course.json",
    "content": "{\n \"actions\": [],\n \"allow_import\": 1,\n \"allow_rename\": 1,\n \"autoname\": \"field:course\",\n \"creation\": \"2024-05-02 16:11:18.869198\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"document_type\": \"Setup\",\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"course\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"course\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Course\",\n   \"oldfieldname\": \"branch\",\n   \"oldfieldtype\": \"Data\",\n   \"reqd\": 1,\n   \"translatable\": 1,\n   \"unique\": 1\n  }\n ],\n \"icon\": \"fa fa-code-fork\",\n \"links\": [],\n \"modified\": \"2026-03-31 14:16:07.519200\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Menu Course\",\n \"naming_rule\": \"By fieldname\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1,\n   \"share\": 1\n  },\n  {\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"share\": 1\n  }\n ],\n \"quick_entry\": 1,\n \"row_format\": \"Dynamic\",\n \"rows_threshold_for_grid_search\": 20,\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_menu_course/ury_menu_course.py",
    "content": "# Copyright (c) 2024, Tridz Technologies Pvt. Ltd and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYMenuCourse(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_menu_item/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_menu_item/ury_menu_item.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-09-13 23:39:59.710031\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item\",\n  \"item_name\",\n  \"rate\",\n  \"special_dish\",\n  \"disabled\",\n  \"course_icon\",\n  \"course\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"item\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item\",\n   \"options\": \"Item\"\n  },\n  {\n   \"fetch_from\": \"item.item_name\",\n   \"fieldname\": \"item_name\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Item Name\"\n  },\n  {\n   \"fieldname\": \"rate\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Rate\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"special_dish\",\n   \"fieldtype\": \"Check\",\n   \"in_list_view\": 1,\n   \"label\": \"Special Dish\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"disabled\",\n   \"fieldtype\": \"Check\",\n   \"in_list_view\": 1,\n   \"label\": \"Disabled\"\n  },\n  {\n   \"fieldname\": \"course\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Course\",\n   \"options\": \"URY Menu Course\"\n  },\n  {\n   \"fieldname\": \"course_icon\",\n   \"fieldtype\": \"Data\",\n   \"in_standard_filter\": 1,\n   \"label\": \"Course Icon\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2025-07-17 12:45:47.721827\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Menu Item\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"row_format\": \"Dynamic\",\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_menu_item/ury_menu_item.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYMenuItem(Document):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_notification_recipient/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_notification_recipient/test_ury_notification_recipient.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYNotificationRecipient(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_notification_recipient/ury_notification_recipient.js",
    "content": "// Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY Notification Recipient', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_notification_recipient/ury_notification_recipient.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-12-12 12:11:12.528305\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"receiver_by_role\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"receiver_by_role\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Receiver By Role\",\n   \"options\": \"Role\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-12-12 12:33:37.646373\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Notification Recipient\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_notification_recipient/ury_notification_recipient.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYNotificationRecipient(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_order/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_order/test_ury_order.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYOrder(FrappeTestCase):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_order/ury_order.js",
    "content": "//  Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n//  For license information, please see license.txt\n\n/**\n * DISCLAIMER:\n * \n * This code implements order-taking app and was developed live in a restaurant  as a quick solution to address immediate needs.\n * \n * Current Usage:\n * - The solution is currently operational in over 8 restaurants.\n * - It handles more than 1000 bills per day.\n * \n * Code Quality:\n * - Initially live-coded as a client script and later moved to the app.\n * - Code may be messy, hard to understand, and not optimally efficient.\n * \n * Maintenance and Future Work:\n * - This solution will continue to be maintained.\n * - A more refined version, built using Vue.js, is available in the URY POS repository and is recommended for future use.\n */\n\nlet array_of_menu = [];\nlet table_html = '';\nlet restaurant_name = '';\nlet currentTable = []\nlet table_list_div = '';\nlet branch_g = \"\"\nlet rstnt_menu_items = '';\nlet total_time = \"\"\n\nfrappe.ui.form.on('URY Order', {\n\tsetup: function (frm) {\n\t\tlet get_item_query = () => {\n\t\t\treturn {\n\t\t\t\tquery: 'ury.ury.doctype.ury_order.ury_order.item_query_restaurant',\n\t\t\t\tfilters: {\n\t\t\t\t\t'table': frm.doc.restaurant_table\n\t\t\t\t}\n\t\t\t};\n\t\t};\n\t\tfrm.set_query('item', 'items', get_item_query);\n\t\tfrm.set_query('add_item', get_item_query);\n\t},\n\n\tonload: function (frm) {\n\t\t$('.ellipsis.title-text').hide();\n\t\tfrappe.call({\n\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.pos_opening_check',\n\t\t\tcallback: function (r) {\n\t\t\t\tif (!r.message) {\n\t\t\t\t\tfrappe.msgprint('Server Error');\n\t\t\t\t}\n\t\t\t\telse if (!r.message.opening_exists) {\n\t\t\t\t\tfrappe.msgprint('POS Opening Entry is not created');\n\t\t\t\t\tdocument.addEventListener('click', function () {\n\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse if (!r.message.cashier || !r.message.pos_profile) {\n\t\t\t\t\tfrappe.msgprint('Incomplete data. Check POS Opening Entry.');\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\tif (r.message.opening_exists) {\n\t\t\t\t\t\t// Set the cashier in URY Order\n\t\t\t\t\t\tfrm.set_value('cashier', r.message.cashier);\n\t\t\t\t\t\tfrm.set_value('waiter', frappe.session.user);\n\t\t\t\t\t\tfrm.set_value('pos_profile', r.message.pos_profile)\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\n\tonload_post_render: function (frm) {\n\n\t\t$('.items-container .item-wrapper').click((e) => {\n\t\t})\n\n\t\t// For adding placeholder in search box\n\t\t$(\"[data-fieldname='item_search']\").addClass(\"item_search\");\n\t\t$(\".item_search\").attr('placeholder', 'Search')\n\n\t},\n\n\trefresh: function (frm) {\n\n\t\tconst check = localStorage.getItem('check');\n\t\tfrm.disable_save();\n\n\t\tfrm.trigger('update_btn');\n\n\t\t$(\"[data-fieldname='no_of_pax']\").on('focus', 'input', function (e) {\n\t\t\t$(this).prop('type', 'number');\n\t\t\t$(this).on('input', function (event) {\n\t\t\t\tlet inputValue = e.target.value;\n\t\t\t\tif (parseInt(inputValue) < 0) {\n\t\t\t\t\t// If negative, set the input value to an empty string\n\t\t\t\t\tevent.target.value = '0';\n\t\t\t\t}\n\t\t\t});\n\t\t})\n\n\t\t$(\"[data-fieldname='qty']\").on('click', 'input', function (e) {\n\t\t\t$(this).prop('type', 'number');\n\t\t})\n\n\t\tfrappe.realtime.on('reload_ro', (data) => {\n\t\t\tif (frm.doc.last_invoice && data.name === frm.doc.last_invoice) {\n\t\t\t\tfrappe.dom.freeze(__('Order Completed'));\n\t\t\t\tfrappe.ui.toolbar.clear_cache();\n\t\t\t}\n\t\t});\n\n\t\t// disable logo click\n\t\tlet logo_nav = document.querySelector('.navbar .navbar-brand')\n\t\tlogo_nav.removeAttribute('href');\n\n\t\t//  button update in window for mobile screen\n\t\tif (window.innerWidth <= 768) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tconst hideButtons = (buttons) => {\n\t\t\t\t\tbuttons.forEach((button) => {\n\t\t\t\t\t\tconst element = document.querySelector(`button[data-label=\"${button}\"].btn-default`);\n\t\t\t\t\t\tif (element) {\n\t\t\t\t\t\t\telement.classList.add('d-none');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t};\n\n\t\t\t\tconst buttonsToHide = ['Table%20Transfer', 'Print', 'Cancel', 'Captain%20Transfer'];\n\t\t\t\thideButtons(buttonsToHide);\n\n\t\t\t\tdocument.querySelector('.custom-actions').classList.remove('hidden-xs', 'hidden-md');\n\t\t\t}, 1000);\n\t\t}\n\n\n\t\tconst handleTableClick = (e) => {\n\t\t\tconst tableId = e.target.id;\n\t\t\tcurrentTable = [tableId];\n\n\t\t\tfrm.set_value('restaurant_table', tableId).then(() => {\n\t\t\t\tfrappe.dom.freeze();\n\n\t\t\t\tconst tabToClick = frm.doc.last_invoice ? '#ury-order-order_tab-tab' : '#ury-order-menu_tab-tab';\n\t\t\t\t$(tabToClick).click();\n\n\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t});\n\n\t\t\tfrm.trigger('menu_listing');\n\t\t\tfrm.trigger('get_menu');\n\t\t\tfrm.trigger('display_menu');\n\t\t};\n\n\t\tconst setTableListHtml = (tableHtml) => {\n\t\t\tconst tableHtmlOpening = '<div class=\"container px-0\"><div class=\"row\" id=\"table_container\">';\n\t\t\tconst tableHtmlClosing = '</div></div>';\n\t\t\tconst tableListHtml = tableHtmlOpening + tableHtml + tableHtmlClosing;\n\n\t\t\t$(frm.fields_dict.table_list.wrapper).html(tableListHtml);\n\t\t\tfrm.page.wrapper.find('.the_table').on('click', handleTableClick);\n\t\t};\n\n\t\tconst setTableHtml = (records) => {\n\t\t\tlet tableHtml = '';\n\t\t\trecords.forEach((tableList) => {\n\t\t\t\tconst tableId = tableList.name;\n\t\t\t\tconst isTableOccupied = tableList.occupied === 1;\n\t\t\t\tconst isCurrentTable = currentTable.includes(tableId);\n\t\t\t\tconst tableTime = tableList.latest_invoice_time;\n\t\t\t\tconst bgColor = isTableOccupied ? 'var(--alert-bg-danger)' : 'var(--alert-bg-success)';\n\n\t\t\t\tlet statusHtml = '';\n\t\t\t\tif (isTableOccupied) {\n\t\t\t\t\tconst transactionTime = moment(tableTime, 'HH:mm');\n\t\t\t\t\tconst currentTime = moment().format('HH:mm');\n\t\t\t\t\tconst minutes = moment(currentTime, 'HH:mm').diff(transactionTime, 'minutes');\n\t\t\t\t\tconst hours = Math.trunc(minutes / 60);\n\t\t\t\t\tconst m1 = minutes % 60;\n\t\t\t\t\tconst totalTime = `${hours}:${m1}`;\n\t\t\t\t\tstatusHtml = `\n\t\t\t\t\t\t<h4 style=\"margin-top:10%;pointer-events:none;\">${tableId}</h4>\n\t\t\t\t\t\t<h6 style=\"pointer-events:none;\">${totalTime}</h6>`;\n\t\t\t\t} else {\n\t\t\t\t\tstatusHtml = `<h4 style=\"margin-top:10%;pointer-events:none;\">${tableId}</h4>`;\n\t\t\t\t}\n\n\t\t\t\tconst backgroundColor = isCurrentTable ? '#73C2FB' : '';\n\t\t\t\tconst tableListDiv = `\n\t\t\t\t\t<div class=\"col-lg-2 col-md-3 col-6 mr-0\">\n\t\t\t\t    <div class=\"border the_table rounded text-center mb-4\"\n\t\t\t\t        style=\"background: ${bgColor};min-height: 100px; background-color: ${backgroundColor};\"\n\t\t\t\t        id=\"${tableId}\">\n\t\t\t\t        ${statusHtml}\n\t\t\t\t    </div>\n\t\t\t\t</div>`;\n\n\t\t\t\ttableHtml += tableListDiv;\n\t\t\t});\n\n\t\t\tsetTableListHtml(tableHtml);\n\t\t};\n\n\t\tfrappe.db.get_list('URY Table', {\n\t\t\tfields: ['name', 'occupied', 'latest_invoice_time', 'restaurant'],\n\t\t\tlimit: 10000\n\t\t}).then((records) => {\n\t\t\trecords.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }));\n\t\t\tsetTableHtml(records);\n\t\t});\n\n\t\t$('#ury-order-menu_tab-tab, #ury-order-order_tab-tab').on('click', function (e) {\n\t\t\tfrm.trigger('get_menu');\n\t\t\tfrm.trigger('display_menu');\n\n\t\t\tif (e.target.id === 'ury-order-menu_tab-tab') {\n\t\t\t\trefresh_field('item');\n\t\t\t}\n\n\t\t});\n\n\t\tfunction handleTabClick(tabId, message) {\n\t\t\t$(\"#\" + tabId).on('click', function (e) {\n\t\t\t\tif (!frm.doc.restaurant_table) {\n\t\t\t\t\tfrappe.msgprint({\n\t\t\t\t\t\ttitle: __('Warning'),\n\t\t\t\t\t\tmessage: __(message),\n\t\t\t\t\t});\n\t\t\t\t\t// Bind a click event to the body to capture clicks outside the message box\n\t\t\t\t\t$('body').one('click', function (event) {\n\t\t\t\t\t\tif (!$(event.target).closest('.modal-dialog').length) {\n\t\t\t\t\t\t\t$(\"#ury-order-table_tab-tab\").click();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\thandleTabClick(\"ury-order-customer_tab-tab\", \"Select Table.\");\n\t\thandleTabClick(\"ury-order-menu_tab-tab\", \"Select Table.\");\n\t\thandleTabClick(\"ury-order-order_tab-tab\", \"Select Table.\");\n\n\t\t$(\"[data-label='Print']\").hide();\n\t\tlocalStorage.removeItem('check');\n\t\tfrm.page.wrapper.find(\".comment-box\").css({ 'display': 'none' });\n\n\t\t// For adding class to all item in menu tab\n\t\t$(\"[data-fieldname='all_item']\").addClass(\"float-right all_item\").css({\n\t\t\t'margin-top': '.5%'\n\t\t});\n\n\t\t// For adding class to priority buttons in menu tab\n\t\t$(\"[data-fieldname='priority_item']\").addClass(\"float-right priority_item\").css({\n\t\t\t'margin-top': '.5%',\n\t\t\t'margin-right': '1%'\n\t\t});\n\n\t\t$(\"[data-fieldname='item_search']\").on(\"click\", function () {\n\t\t\tfrm.set_value('item_search', '');\n\t\t\tfrm.trigger('get_menu');\n\t\t\tfrm.trigger('display_menu');\n\t\t\trefresh_field('item');\n\t\t});\n\t\tif (frm.doc.last_invoice) {\n\t\t\tfrm.events.cancel_order(frm)\n\t\t\tfrm.events.table_transfer(frm)\n\t\t\tfrm.events.captain_transfer(frm)\n\t\t\tfrm.events.print(frm)\n\t\t}\n\t},\n\n\titem_search(frm) {\n\t\tfrm.trigger('get_menu');\n\t},\n\n\tmenu_listing: function (frm, index) {\n\t\tfrappe.call({\n\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.get_restaurant_and_menu_name',\n\t\t\targs: {\n\t\t\t\ttable: frm.doc.restaurant_table\n\t\t\t},\n\t\t\tcallback: (r) => {\n\t\t\t\tarray_of_menu = []\n\t\t\t\tfrappe.db.get_doc('URY Menu', r.message[1]).then(item_list => {\n\t\t\t\t\titem_list.items.forEach(menu_item => {\n\t\t\t\t\t\tif (menu_item.disabled == 0) {\n\t\t\t\t\t\t\tlet menu_item_list = []\n\t\t\t\t\t\t\tmenu_item_list['item_name'] = menu_item.item_name\n\t\t\t\t\t\t\tmenu_item_list['item_code'] = menu_item.item\n\t\t\t\t\t\t\tmenu_item_list['rate_of_item'] = menu_item.rate\n\t\t\t\t\t\t\tmenu_item_list['special_dish_menu'] = menu_item.special_dish\n\t\t\t\t\t\t\tarray_of_menu = [...array_of_menu, menu_item_list]\n\t\t\t\t\t\t\tfrm.events.quantity_add(frm, index, menu_item_list);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t});\n\t},\n\n\tcalculate_total: function (frm, price, qty) {\n\t\tlet total = frm.doc.grand_total\n\t\tlet grand_total = total + (price * qty)\n\t\tcur_frm.set_value(\"grand_total\", grand_total);\n\t},\n\n\tdisplay_menu: function (frm) {\n\t\t$('#restaurant_menu_items').empty()\n\t\tif (array_of_menu.length > 0) {\n\t\t\tarray_of_menu.map((menu_item_list, index) => {\n\t\t\t\tfrm.events.item_list_card(frm, index, menu_item_list);\n\t\t\t\tfrm.events.quantity_add(frm, index, menu_item_list);\n\t\t\t\tfrm.events.qty_comment_edit(frm, index, menu_item_list);\n\t\t\t\tlet child_tab_item = frm.doc.items\n\t\t\t\tchild_tab_item.forEach(child_items => {\n\t\t\t\t\tif (menu_item_list.item_name === child_items.item_name) {\n\t\t\t\t\t\t$(`#${index}_input`).val(child_items.qty)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t})\n\t\t}\n\t},\n\n\titem_list_card: function (frm, index, menu_item_list) {\n\t\trstnt_menu_items = `\n\t            <div class=\"col-lg-2 col-md-3 col-6 mr-0\">\n                    <div class=\"border border-light mb-4 mh-100 rounded text-center\" id=${index} style=\"box-shadow: 0 0px 3px 0 rgba(0, 0, 0, 0.2);\">\n                        <div class=\"mt-3\" style=\"padding: 0 13px;text-overflow: ellipsis;overflow:hidden;white-space:nowrap;width: 160px;\">${menu_item_list.item_name}</div>\n                        <div> ₹ ${menu_item_list.rate_of_item}</div>\n                        <div class=\"d-none\">${menu_item_list.special_dish_menu}</div>\n                        <div class=\"d-flex \" style=\"margin-bottom:10%;margin-top:10%;margin-left:8%;margin-right:8%;\">\n                            <div class=\"input-group\">\n                                <div class=\"input-group-prepend\">\n                                    <<button class=\"btn btn-secondary  rounded-0 shadow-none\" style=\"margin-left:-21%;\" id=${index + '_min'}> - </button>\n                                    </div>\n                                    <input class=\"form-control rounded-0 shadow-none text-center\" style=\"height:36px;margin-right:-10px;margin-left:-12%;\" id=${index + '_input'} value=\"0\" readonly />\n                                    <div class=\"input-group-append\">\n                                      <button class=\"btn btn-secondary rounded-0 shadow-none\" style=\"margin-left:20%;\" id=${index + '_add'}> + </button>\n                                </div>\n                            </div>                \n                        </div>\n                    </div>\n                </div>`\n\t\t$('#restaurant_menu_items').append(rstnt_menu_items);\n\t},\n\n\tqty_comment_edit: function (frm, index, menu_item_list) {\n\t\tfrm.page.wrapper.find('#' + index + '_input').on(\"click\", function (evt) {\n\t\t\tfrm.events.dialogApiForQtyCommentEdit(frm, index, menu_item_list);\n\t\t})\n\t},\n\n\tdialogApiForQtyCommentEdit: function (frm, index, menu_item_list) {\n\t\tlet OrderItems = menu_item_list.item_code\n\t\tlet names = menu_item_list.item_name\n\t\tvar added = false;\n\t\tlet d = new frappe.ui.Dialog({\n\t\t\ttitle: 'Enter details',\n\t\t\tfields: [\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Quantity',\n\t\t\t\t\tfieldname: 'qty',\n\t\t\t\t\tfieldtype: 'Int',\n\t\t\t\t\ttype: 'number',\n\t\t\t\t\tdefault: $(`#${OrderItems}_cartqty`).val()\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Comment',\n\t\t\t\t\tfieldname: 'comment',\n\t\t\t\t\tfieldtype: 'Data',\n\t\t\t\t\tdefault: $(`#${OrderItems}_comment`).val()\n\t\t\t\t}\n\t\t\t],\n\t\t\tprimary_action_label: 'Add',\n\t\t\tprimary_action(values) {\n\t\t\t\td.hide();\n\t\t\t\t(frm.doc.items || []).forEach((d) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\td.item === OrderItems &&\n\t\t\t\t\t\tvalues.qty !== null &&\n\t\t\t\t\t\tvalues.qty !== undefined &&\n\t\t\t\t\t\tvalues.qty !== \"\" &&\n\t\t\t\t\t\tvalues.qty > 0\n\t\t\t\t\t) {\n\t\t\t\t\t\td.qty = values.qty;\n\t\t\t\t\t\tconst oldqty = $(`#${OrderItems}_cartqty`).val()\n\t\t\t\t\t\t$(`#${index}_input`).val(values.qty)\n\t\t\t\t\t\t$(`#${OrderItems}_cartqty`).val(`${d.qty}`);\n\t\t\t\t\t\t$(`#${OrderItems}_comment`).val(values.comment)\n\t\t\t\t\t\td.comment = values.comment\n\t\t\t\t\t\tadded = true;\n\t\t\t\t\t\tfrappe.show_alert({\n\t\t\t\t\t\t\tmessage: __('Item Added Total Qty= ' + d.qty + ''),\n\t\t\t\t\t\t\tindicator: 'green'\n\t\t\t\t\t\t}, 0.85);\n\n\t\t\t\t\t\tconst added_qty = d.qty - parseInt(oldqty)\n\t\t\t\t\t\tfrm.events.calculate_total(frm, menu_item_list.rate_of_item, added_qty);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn frappe.run_serially([\n\t\t\t\t\t() => {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!added &&\n\t\t\t\t\t\t\tvalues.qty !== null &&\n\t\t\t\t\t\t\tvalues.qty !== undefined &&\n\t\t\t\t\t\t\tvalues.qty !== \"\" &&\n\t\t\t\t\t\t\tvalues.qty > 0\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t$(`#${index}_input`).val(values.qty)\n\t\t\t\t\t\t\tfrappe.show_alert({\n\t\t\t\t\t\t\t\tmessage: __('Item Added Total Qty= 1'),\n\t\t\t\t\t\t\t\tindicator: 'green'\n\t\t\t\t\t\t\t}, 0.85);\n\n\t\t\t\t\t\t\tfrm.events.calculate_total(frm, menu_item_list.rate_of_item, values.qty);\n\t\t\t\t\t\t\tfrm.events.createCartItem(frm, OrderItems, names, values.qty, values.comment);\n\t\t\t\t\t\t\treturn frm.add_child('items', {\n\t\t\t\t\t\t\t\titem: OrderItems,\n\t\t\t\t\t\t\t\titem_name: names,\n\t\t\t\t\t\t\t\tqty: values.qty,\n\t\t\t\t\t\t\t\tcomment: values.comment\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t() => frm.get_field(\"items\").refresh()\n\t\t\t\t]);\n\t\t\t}\n\t\t});\n\t\td.show();\n\t},\n\n\tquantity_add: function (frm, index, menu_item_list) {\n\t\tlet OrderItems = menu_item_list.item_code;\n\t\tfrm.page.wrapper.find('#' + index + '_add').on(\"click\", function (evt) {\n\t\t\tlet OrderItems = menu_item_list.item_code;\n\t\t\tlet names = menu_item_list.item_name;\n\t\t\tvar added = false;\n\t\t\t(frm.doc.items || []).forEach((d) => {\n\t\t\t\tif (d.item === OrderItems) {\n\t\t\t\t\tlet qty = d.qty += 1;\n\t\t\t\t\t$(`#${index}_input`).val(d.qty);\n\t\t\t\t\t$(`#${OrderItems}_cartqty`).val(qty);\n\t\t\t\t\tadded = true;\n\t\t\t\t\tfrappe.show_alert({\n\t\t\t\t\t\tmessage: __('Item Added Total Qty= ' + d.qty + ''),\n\t\t\t\t\t\tindicator: 'green'\n\t\t\t\t\t}, 0.85);\n\n\t\t\t\t\tfrm.events.calculate_total(frm, menu_item_list.rate_of_item, 1);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn frappe.run_serially([\n\t\t\t\t() => {\n\t\t\t\t\tif (!added) {\n\t\t\t\t\t\t$(`#${index}_input`).val(1);\n\t\t\t\t\t\tfrappe.show_alert({\n\t\t\t\t\t\t\tmessage: __('Item Added Total Qty= 1'),\n\t\t\t\t\t\t\tindicator: 'green'\n\t\t\t\t\t\t}, 0.85);\n\n\t\t\t\t\t\tfrm.events.calculate_total(frm, menu_item_list.rate_of_item, 1);\n\n\t\t\t\t\t\tfrm.events.createCartItem(frm, OrderItems, names, 1);\n\t\t\t\t\t\treturn frm.add_child('items', {\n\t\t\t\t\t\t\titem: OrderItems,\n\t\t\t\t\t\t\titem_name: names,\n\t\t\t\t\t\t\tqty: 1\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => frm.get_field(\"items\").refresh()\n\t\t\t]);\n\t\t});\n\n\n\t\tfrm.page.wrapper.find('#' + OrderItems + '_cartqty').off(\"click\").on(\"click\", function (evt) {\n\t\t\tfrm.events.dialogApiForQtyCommentEdit(frm, index, menu_item_list);\n\t\t});\n\n\t\tfrm.page.wrapper.find('#' + OrderItems + '_remove').on(\"click\", function (evt) {\n\t\t\tlet OrderItems = menu_item_list.item_code;\n\t\t\t// Remove the item from frm.doc.items\n\t\t\tfrm.doc.items = (frm.doc.items || []).filter((d) => d.item !== OrderItems);\n\n\t\t\tconst qty = $(`#${OrderItems}_cartqty`).val()\n\n\t\t\tif (qty) {\n\t\t\t\tconst negativeQty = qty * -1;\n\t\t\t\tfrm.events.calculate_total(frm, menu_item_list.rate_of_item, negativeQty);\n\n\t\t\t}\n\n\t\t\tfrm.refresh_field(\"items\");\n\t\t\t$(`#${OrderItems}_container`).remove();\n\t\t\t$(`#${OrderItems}_qtyContainer`).remove();\n\t\t\t$(`#${OrderItems}_removeBtn`).remove()\n\t\t});\n\n\t\tfrm.page.wrapper.find('#' + index + '_min').on(\"click\", function (evt) {\n\t\t\tlet OrderItems = menu_item_list.item_code;\n\t\t\tvar removed = false;\n\t\t\t(frm.doc.items || []).forEach((d) => {\n\t\t\t\tif (d.item === OrderItems) {\n\t\t\t\t\td.qty -= 1;\n\t\t\t\t\t$(`#${index}_input`).val(d.qty);\n\t\t\t\t\tif (d.qty < 1) {\n\t\t\t\t\t\t$(`#${index}_input`).val(0);\n\t\t\t\t\t\td.qty = 0;\n\t\t\t\t\t\tremoved = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfrappe.show_alert({\n\t\t\t\t\t\t\tmessage: __('Item Quantity Reduced Total Qty= ' + d.qty + ''),\n\t\t\t\t\t\t\tindicator: 'green'\n\t\t\t\t\t\t}, 0.85);\n\t\t\t\t\t\t$(`#${OrderItems}_cartqty`).val(`${d.qty}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tfrm.events.calculate_total(frm, menu_item_list.rate_of_item, -1);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tlet items = frm.doc.items || [];\n\t\t\tlet new_items = [];\n\n\t\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\t\tif (items[i].qty !== 0) {\n\t\t\t\t\tnew_items.push(items[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfrm.set_value(\"items\", new_items);\n\t\t\tfrm.refresh_field(\"items\");\n\n\t\t\treturn frappe.run_serially([\n\t\t\t\t() => {\n\t\t\t\t\tfrm.get_field(\"items\").refresh();\n\t\t\t\t\tif (removed) {\n\t\t\t\t\t\t$(`#${OrderItems}_container`).remove();\n\t\t\t\t\t\t$(`#${OrderItems}_qtyContainer`).remove();\n\t\t\t\t\t\t$(`#${OrderItems}_removeBtn`).remove();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]);\n\t\t});\n\n\t},\n\n\tcreateCartItem: function (frm, OrderItems, names, qty, comment) {\n\t\tlet restaurantCartItemsList = $('#restaurantCartItemsList');\n\t\tif (restaurantCartItemsList.length === 0) {\n\t\t\trestaurantCartItemsList = $('<div id=\"restaurantCartItemsList\" class=\"row\"></div>');\n\t\t\tlet heading = `\n            <div class=\"col-6 py-3\">\n                <h5><strong>Item Name</strong></h5>\n            </div>\n            <div class=\"col-3 py-3\">\n                <h5><strong>Quantity</strong></h5>\n            </div>\n            <div class=\"col-3 py-3\"></div>\n            `;\n\t\t\trestaurantCartItemsList.append(heading);\n\t\t\t$('#restaurantCartItems').append(restaurantCartItemsList);\n\t\t}\n\n\t\tlet itemDetails = `\n            <div class=\"col-6 py-3\" id=\"${OrderItems}_container\">\n                <h5>${names}</h5>\n            </div>\n            <div class=\"col-3 py-3\" id=\"${OrderItems}_qtyContainer\">\n                <input class=\"input-with-feedback form-control text-center\" style=\"background: var(--control-bg) !important\" id=\"${OrderItems}_cartqty\" value=\"${qty}\" readonly/>\n            </div>\n            <input id=\"${OrderItems}_comment\" class=\"d-none\" value=\"${comment || ''}\"></input>\n            <div class=\"col-3 py-3\" id=\"${OrderItems}_removeBtn\">\n                <button type=\"button\" class=\"btn\" id=\"${OrderItems}_remove\">\n                    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-trash\" viewBox=\"0 0 16 16\">\n                        <path d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z\"></path>\n                        <path d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z\"></path>\n                    </svg>\n                </button>\n            </div>`;\n\n\t\t$(`#${OrderItems}_cartqty`).val(qty);\n\t\trestaurantCartItemsList.append(itemDetails);\n\t},\n\n\tget_menu: function (frm) {\n\t\tlet search_input = frm.doc?.item_search ? frm.doc.item_search : undefined\n\t\t$('#restaurant_menu_items').empty()\n\t\tarray_of_menu == [] ? frm.trigger('menu_listing') : ''\n\t\tif (search_input !== undefined) {\n\t\t\tif (array_of_menu.length > 0) {\n\t\t\t\tarray_of_menu.map((menu_item_list, index) => {\n\t\t\t\t\tif ((menu_item_list.item_name.toLowerCase().indexOf(search_input != undefined ? search_input.toLowerCase() : undefined) !== -1) ||\n\t\t\t\t\t\t(menu_item_list.item_code.toLowerCase().indexOf(search_input != undefined ? search_input.toLowerCase() : undefined) !== -1)) {\n\t\t\t\t\t\tfrm.events.item_list_card(frm, index, menu_item_list);\n\t\t\t\t\t\tfrm.events.quantity_add(frm, index, menu_item_list);\n\t\t\t\t\t\tfrm.events.qty_comment_edit(frm, index, menu_item_list);\n\t\t\t\t\t\tlet child_tab_item = frm.doc.items\n\t\t\t\t\t\tchild_tab_item.forEach(child_items => {\n\t\t\t\t\t\t\tif (menu_item_list.item_name === child_items.item_name) {\n\t\t\t\t\t\t\t\t$(`#${index}_input`).val(child_items.qty)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t},\n\n\tall_item: function (frm) {\n\t\t$(\"body\").on('click', '.all_item', function (e) {\n\t\t\t// frm.trigger('menu_listing')\n\t\t\tfrm.trigger('display_menu')\n\t\t\t$(\"[data-fieldname='item_search']\").val('');\n\t\t\t$('.all_item').addClass('btn-outline-primary')\n\t\t})\n\t},\n\n\tpriority_item: function (frm) {\n\t\t$(\"body\").on('click', '.priority_item', function (e) {\n\t\t\t$('#restaurant_menu_items').empty()\n\t\t\t$(\"[data-fieldname='item_search']\").val('');\n\t\t\tarray_of_menu == [] ? frm.trigger('menu_listing') : ''\n\t\t\tif (array_of_menu.length > 0) {\n\t\t\t\tarray_of_menu.map((menu_item_list, index) => {\n\t\t\t\t\tif (menu_item_list.special_dish_menu === 1) {\n\t\t\t\t\t\tfrm.events.item_list_card(frm, index, menu_item_list);\n\t\t\t\t\t\tfrm.events.quantity_add(frm, index, menu_item_list);\n\t\t\t\t\t\tfrm.events.qty_comment_edit(frm, index, menu_item_list);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tfrm.trigger('display_menu');\n\t\t\t}\n\t\t\t$('.priority_item').addClass('btn-outline-primary')\n\t\t})\n\t},\n\n\tclear: function (frm) {\n\t\tfrm.doc.customer_name = '';\n\t\tfrm.doc.no_of_pax = '';\n\t\tfrm.doc.add_item = '';\n\t\tfrm.doc.grand_total = 0;\n\t\tfrm.doc.items = [];\n\t\tfrm.doc.modified_time = '';\n\t\tfrm.refresh_field(\"customer_name\");\n\t\tfrm.refresh_field(\"no_of_pax\");\n\t\tfrm.refresh_field(\"modified_time\")\n\t},\n\n\trestaurant_table: function (frm) {\n\t\t// to show selected table in the view\n\t\tconst activeTable = document.createElement(\"div\");\n\t\tactiveTable.innerHTML = `<span style=\"margin-left:1.2rem;margin-top:3rem;font-size:16px;font-weight:600\">${frm.doc.restaurant_table}</span>`;\n\t\tactiveTable.style.color = \"#1034A6\"; // Set color to blue\n\n\t\tconst existingSpans = document.querySelectorAll(\".page-head-content span\");\n\t\texistingSpans.forEach((span) => span.remove());\n\n\t\tconst formPageDiv = document.querySelector(\".page-head-content\");\n\t\tformPageDiv.insertBefore(activeTable, formPageDiv.firstChild);\n\n\n\t\t// select the open sales order items for this table\n\t\tif (!frm.doc.restaurant_table) {\n\t\t\treturn;\n\t\t}\n\t\treturn frappe.call({\n\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.get_order_invoice',\n\t\t\targs: {\n\t\t\t\ttable: frm.doc.restaurant_table,\n\t\t\t},\n\t\t\tcallback: (r) => {\n\t\t\t\tfrm.events.set_invoice_items(frm, r);\n\t\t\t}\n\n\t\t});\n\n\t},\n\n\tupdate_btn: async function (frm) {\n\t\tconst check = localStorage.getItem('check');\n\t\tfrm.disable_save();\n\n\t\tconst handleUpdate = async () => {\n\t\t\tif (frm.doc.restaurant_table) {\n\t\t\t\tlocalStorage.setItem('check', 'printed');\n\t\t\t\tfrm.remove_custom_button('Update');\n\t\t\t\t$('.standard-actions').addClass('hidden-xs hidden-md');\n\n\t\t\t\tif (frm.doc.pos_profile) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfrm.trigger('sync');\n\t\t\t\t\t\t$('.standard-actions').removeClass('hidden-xs hidden-md');\n\t\t\t\t\t\tfrm.doc.comments = '';\n\t\t\t\t\t\tlocalStorage.removeItem('check');\n\t\t\t\t\t}\n\t\t\t\t\tcatch (error) {\n\t\t\t\t\t\tconsole.error(error);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\tmessage: __('POS Profile Not Found or User permission in POS Profile is not given to this user')\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfrappe.throw({\n\t\t\t\t\tmessage: __('Select Table')\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tif (frm.doc.last_invoice) {\n\t\t\tconst pos_invoice = await frappe.db.get_doc('POS Invoice', frm.doc.last_invoice);\n\t\t\tif (pos_invoice.invoice_printed === 0 && !check) {\n\t\t\t\tfrm.add_custom_button(__('Update'), handleUpdate);\n\t\t\t}\n\t\t} else {\n\t\t\tfrm.add_custom_button(__('Update'), handleUpdate);\n\t\t}\n\t},\n\n\n\tsync: function (frm) {\n\t\t$('.custom-actions').addClass('hidden-xs hidden-md');\n\t\tif (frm.doc.customer_name && frm.doc.no_of_pax) {\n\t\t\tlet last_invoice = ''\n\t\t\tif (frm.doc.last_invoice) {\n\t\t\t\tlast_invoice = frm.doc.last_invoice\n\t\t\t}\n\t\t\tif ((frm.doc.items).length !== 0) {\n\t\t\t\t$('#ury-order-order_tab-tab').click();\n\t\t\t\tfrappe.call({\n\t\t\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.sync_order',\n\t\t\t\t\targs: {\n\t\t\t\t\t\ttable: frm.doc.restaurant_table,\n\t\t\t\t\t\titems: frm.doc.items,\n\t\t\t\t\t\tmode_of_payment: \"Cash\",\n\t\t\t\t\t\tcustomer: frm.doc.customer_name,\n\t\t\t\t\t\tno_of_pax: frm.doc.no_of_pax,\n\t\t\t\t\t\twaiter: frappe.session.user,\n\t\t\t\t\t\tpos_profile: frm.doc.pos_profile,\n\t\t\t\t\t\tlast_modified_time: frm.doc.modified_time,\n\t\t\t\t\t\tcashier: frm.doc.cashier,\n\t\t\t\t\t\tlast_invoice: last_invoice,\n\t\t\t\t\t\tcomments: frm.doc.comments,\n\t\t\t\t\t\torder_type: frm.doc.order_type,\n\t\t\t\t\t},\n\t\t\t\t\tcallback: (r) => {\n\t\t\t\t\t\tlet invoice = r.message;\n\t\t\t\t\t\tif (invoice.status == \"Failure\") {\n\t\t\t\t\t\t\tfrappe.dom.freeze();\n\t\t\t\t\t\t\tdocument.addEventListener('click', function () {\n\t\t\t\t\t\t\t\twindow.location.reload()\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tcur_frm.set_value(\"modified_time\", invoice.modified);\n\t\t\t\t\t\t\tfrm.events.set_invoice_items(frm, r);\n\t\t\t\t\t\t\tfrappe.show_alert({ message: __('Order Updated'), indicator: 'green' });\n\t\t\t\t\t\t\tlocalStorage.removeItem('check');\n\t\t\t\t\t\t\tfrm.trigger('update_btn');\n\t\t\t\t\t\t\t$('.standard-actions').removeClass('hidden-xs hidden-md');\n\t\t\t\t\t\t\tfrm.trigger('refresh');\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfrm.trigger('update_btn');\n\t\t\t\t$('.standard-actions').removeClass('hidden-xs hidden-md');\n\t\t\t\tlocalStorage.removeItem('check');\n\t\t\t\tfrappe.throw({\n\t\t\t\t\tmessage: __(\"Select Items\")\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfrm.trigger('update_btn');\n\t\t\t$('.standard-actions').removeClass('hidden-xs hidden-md');\n\t\t\tlocalStorage.removeItem('check');\n\t\t\tfrappe.throw({\n\t\t\t\tmessage: __(\"Select Customer / No of Pax\")\n\t\t\t});\n\t\t}\n\n\t},\n\n\tprint: function (frm) {\n\t\tfrappe.db.get_doc('POS Invoice', frm.doc.last_invoice).then(pos_invoice => {\n\t\t\tif (pos_invoice.invoice_printed === 0) {\n\t\t\t\tfrm.add_custom_button(__('Print'), () => {\n\t\t\t\t\tlet invoice = frm.doc.last_invoice\n\t\t\t\t\tfrappe.db.get_doc('POS Invoice', invoice).then(pos_invoice => {\n\t\t\t\t\t\tif (pos_invoice.invoice_printed == 1) {\n\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\ttitle: __(\"Invoice Already Billed\"),\n\t\t\t\t\t\t\t\tmessage: __(\"This order has already been billed. Please reload the page.\"),\n\t\t\t\t\t\t\t\tindicator: 'red'\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfrappe.dom.freeze(__('Printing Invoice'));\n\t\t\t\t\t\tfrappe.db.get_doc('POS Profile', pos_invoice.pos_profile).then(profile => {\n\n\t\t\t\t\t\t\tif (profile.qz_print == 1) {\n\t\t\t\t\t\t\t\t// To fetch qz_key from site config\n\t\t\t\t\t\t\t\tfrappe.call({\n\t\t\t\t\t\t\t\t\tmethod: \"ury.ury.api.ury_print.qz_certificate\",\n\t\t\t\t\t\t\t\t\tcallback: function (response) {\n\t\t\t\t\t\t\t\t\t\tif (response.message) {\n\t\t\t\t\t\t\t\t\t\t\tvar qzKey = response.message;\n\t\t\t\t\t\t\t\t\t\t\tqz.security.setCertificatePromise(function (resolve, reject) {\n\t\t\t\t\t\t\t\t\t\t\t\t//Preferred method - from server\n\t\t\t\t\t\t\t\t\t\t\t\tfetch(\"/private/\" + qzKey, { cache: 'no-store', headers: { 'Content-Type': 'text/plain' } })\n\t\t\t\t\t\t\t\t\t\t\t\t\t.then(function (data) { data.ok ? resolve(data.text()) : reject(data.text()); });\n\t\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tfrappe.call({\n\t\t\t\t\t\t\t\t\tmethod: \"frappe.www.printview.get_html_and_style\",\n\t\t\t\t\t\t\t\t\targs: {\n\t\t\t\t\t\t\t\t\t\tdoc: \"POS Invoice\",\n\t\t\t\t\t\t\t\t\t\tname: invoice,\n\t\t\t\t\t\t\t\t\t\tprint_format: profile.print_format,\n\t\t\t\t\t\t\t\t\t\t_lang: 'en',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tcallback: function (r) {\n\t\t\t\t\t\t\t\t\t\tif (qz.websocket.isActive()) {\n\t\t\t\t\t\t\t\t\t\t\t// Use the existing connection to print\n\t\t\t\t\t\t\t\t\t\t\tprintWithQZTray();\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t// Establish a new connection and then print\n\t\t\t\t\t\t\t\t\t\t\tqz.websocket.connect({ host: profile.qz_host, usingSecure: false })\n\t\t\t\t\t\t\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// test();\n\t\t\t\t\t\t\t\t\t\t\t\t\tprintWithQZTray();\n\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t.catch((error) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Handle connection error\n\t\t\t\t\t\t\t\t\t\t\t\t\tconsole.error(\"Error connecting to QZ Tray:\", error);\n\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmessage: __(\"Printing Failed: Error connecting to QZ Tray\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tfunction printWithQZTray() {\n\t\t\t\t\t\t\t\t\t\t\tqz.printers.getDefault()\n\t\t\t\t\t\t\t\t\t\t\t\t.then((printer) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tvar htmlcontent = r.message.html;\n\t\t\t\t\t\t\t\t\t\t\t\t\tvar data = [{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: 'html',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: 'plain',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata: htmlcontent\n\t\t\t\t\t\t\t\t\t\t\t\t\t}];\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tvar config = qz.configs.create(printer);\n\t\t\t\t\t\t\t\t\t\t\t\t\tqz.print(config, data)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.then(function () {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.call({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmethod: `ury.ury.api.ury_print.qz_print_update`,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\targs: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tinvoice: invoice\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcallback: function (r) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.show_alert({ message: __('Invoice Printed'), indicator: 'green' });\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t$('.standard-actions').addClass('hidden-xs hidden-md');\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.ui.toolbar.clear_cache()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdocument.addEventListener('click', function () {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.ui.toolbar.clear_cache()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.catch(function (error) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Handle printing error\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconsole.error(\"Error printing with QZ Tray:\", error);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmessage: __(\"Printing Failed: Error printing with QZ Tray\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t.catch(function (error) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Handle printer lookup error\n\t\t\t\t\t\t\t\t\t\t\t\t\tconsole.error(\"Error looking up printer:\", error);\n\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmessage: __(\"Printing Failed: Error looking up printer\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\telse if (profile.printer_settings.some(e => e.bill == 1)) {\n\t\t\t\t\t\t\t\tfrappe.call({\n\t\t\t\t\t\t\t\t\tmethod: `ury.ury.api.ury_print.select_network_printer`,\n\t\t\t\t\t\t\t\t\targs: {\n\t\t\t\t\t\t\t\t\t\tpos_profile: pos_invoice.pos_profile,\n\t\t\t\t\t\t\t\t\t\tinvoice_id: invoice\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tcallback: function (r) {\n\t\t\t\t\t\t\t\t\t\tif (r.message == \"Success\") {\n\t\t\t\t\t\t\t\t\t\t\t$('.standard-actions').addClass('hidden-xs hidden-md');\n\t\t\t\t\t\t\t\t\t\t\tfrappe.show_alert({ message: __('Invoice Printed'), indicator: 'green' });\n\t\t\t\t\t\t\t\t\t\t\tsetTimeout(function () {\n\t\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\t\t\tfrappe.ui.toolbar.clear_cache()\n\t\t\t\t\t\t\t\t\t\t\t}, 1500)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\t\tconsole.error(r.message);\n\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: __(\"Printing Failed\")\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\terror: function (xhr, textStatus, error) {\n\t\t\t\t\t\t\t\t\t\tconsole.error(\"AJAX Error:\", error); // Log the AJAX error\n\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\t\tmessage: __(\"An error occurred while printing\")\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tfrappe.call({\n\t\t\t\t\t\t\t\t\tmethod: `ury.ury.api.ury_print.print_pos_page`,\n\t\t\t\t\t\t\t\t\targs: {\n\t\t\t\t\t\t\t\t\t\tdoctype: \"POS Invoice\",\n\t\t\t\t\t\t\t\t\t\tname: invoice,\n\t\t\t\t\t\t\t\t\t\tprint_format: profile.print_format\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tcallback: function (r) {\n\t\t\t\t\t\t\t\t\t\t$('.standard-actions').addClass('hidden-xs hidden-md');\n\t\t\t\t\t\t\t\t\t\tfrappe.ui.toolbar.clear_cache()\n\t\t\t\t\t\t\t\t\t\tfrappe.dom.unfreeze();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t}).addClass(\"print-btn\");\n\t\t\t}\n\t\t})\n\t},\n\n\tset_invoice_items: function (frm, r) {\n\n\t\tlet invoice = r.message;\n\n\t\tif (invoice.name) {\n\t\t\tif (frm.doc.pos_profile) {\n\t\t\t\tfrappe.call({\n\t\t\t\t\tmethod: 'frappe.client.get',\n\t\t\t\t\targs: {\n\t\t\t\t\t\tdoctype: 'POS Profile',\n\t\t\t\t\t\tname: frm.doc.pos_profile\n\t\t\t\t\t},\n\t\t\t\t\tcallback: function (response) {\n\t\t\t\t\t\tif (response.message) {\n\t\t\t\t\t\t\tvar transfer_roles = response.message.transfer_role_permissions.map(role => role.role);\n\t\t\t\t\t\t\tvar user_roles = frappe.user_roles;\n\t\t\t\t\t\t\tvar has_access = transfer_roles.some(role => user_roles.includes(role));\n\t\t\t\t\t\t\tif (has_access == false) {\n\t\t\t\t\t\t\t\tif (invoice.waiter && invoice.waiter != frappe.session.user) {\n\t\t\t\t\t\t\t\t\tfrappe.db.get_doc('User', invoice.waiter)\n\t\t\t\t\t\t\t\t\t\t.then(docs => {\n\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.freeze();\n\t\t\t\t\t\t\t\t\t\t\tdocument.addEventListener('click', function () {\n\t\t\t\t\t\t\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\t\t\ttitle: __(\"Table Assignment Error\"),\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: __(\"This table is assigned to {0}. Please contact them for assistance.\", [docs.full_name])\n\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tfrappe.show_alert({\n\t\t\t\t\t\t\t\t\t\tmessage: __('Past Order Fetched'),\n\t\t\t\t\t\t\t\t\t\tindicator: 'green'\n\t\t\t\t\t\t\t\t\t}, 5);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfrappe.throw({\n\t\t\t\t\ttitle: __(\"Validation Error\"),\n\t\t\t\t\tmessage: __(\"POS Profile Failed to Load\")\n\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfrm.trigger('clear');\n\t\t}\n\t\tfrappe.dom.freeze(__('Setting table'));\n\n\t\tfrm.doc.items = [];\n\t\t(invoice.items || []).forEach((d) => {\n\t\t\tfrm.add_child('items', { item: d.item_code, item_name: d.item_name, qty: d.qty, rate: d.rate });\n\t\t});\n\n\t\tfrm.set_value('customer_name', invoice.customer);\n\t\tfrm.set_value('pos_profile', invoice.pos_profile);\n\t\tfrm.set_value('no_of_pax', invoice.no_of_pax);\n\t\tfrm.set_value('grand_total', invoice.grand_total);\n\t\tfrm.set_value('last_invoice', invoice.name);\n\t\tfrm.set_value('modified_time', invoice.modified);\n\n\t\tif (frm.doc.customer_name) {\n\t\t\tfrm.trigger('favourite')\n\t\t}\n\n\t\tconst tabToClick = frm.doc.last_invoice ? '#ury-order-order_tab-tab' : '#ury-order-menu_tab-tab';\n\t\t$(tabToClick).click();\n\n\t\tif (frm.doc.last_invoice) {\n\t\t\tfrappe.db.get_doc('POS Invoice', frm.doc.last_invoice).then(invoice => {\n\t\t\t\tinvoice.items.forEach(item => {\n\t\t\t\t\tfrm.events.createCartItem(frm, item.item_code, item.item_name, item.qty);\n\n\t\t\t\t});\n\t\t\t});\n\t\t\tfrm.trigger('menu_listing')\n\t\t}\n\t\tfrm.refresh();\n\t\tfrappe.dom.unfreeze();\n\t},\n\n\ttable_transfer: function (frm) {\n\t\tfrm.add_custom_button(__('Table Transfer'), () => {\n\t\t\tfrappe.db.get_doc('POS Invoice', frm.doc.last_invoice).then((pos_invoice) => {\n\t\t\t\tif (pos_invoice.invoice_printed === 1) {\n\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\ttitle: __(\"Invoice Already Billed\"),\n\t\t\t\t\t\tmessage: __(\"This order has already been billed. Please reload the page.\"),\n\t\t\t\t\t\tindicator: 'red'\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\telse {\n\t\t\t\t\tconst d = new frappe.ui.Dialog({\n\t\t\t\t\t\ttitle: 'Transfer Table',\n\t\t\t\t\t\tfields: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlabel: 'New Table',\n\t\t\t\t\t\t\t\tfieldname: 'table',\n\t\t\t\t\t\t\t\tfieldtype: 'Link',\n\t\t\t\t\t\t\t\toptions: 'URY Table',\n\t\t\t\t\t\t\t\tget_query: function () {\n\t\t\t\t\t\t\t\t\treturn { filters: { \"occupied\": 0 } };\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlabel: 'Current Table',\n\t\t\t\t\t\t\t\tfieldname: 'cur_table',\n\t\t\t\t\t\t\t\tfieldtype: 'Data',\n\t\t\t\t\t\t\t\tread_only: true,\n\t\t\t\t\t\t\t\tdefault: frm.doc.restaurant_table\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\tprimary_action_label: 'Transfer',\n\t\t\t\t\t\tprimary_action(values) {\n\t\t\t\t\t\t\tfrappe.dom.freeze();\n\t\t\t\t\t\t\tfrm.call({\n\t\t\t\t\t\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.table_transfer',\n\t\t\t\t\t\t\t\targs: {\n\t\t\t\t\t\t\t\t\tinvoice: frm.doc.last_invoice,\n\t\t\t\t\t\t\t\t\ttable: frm.doc.restaurant_table,\n\t\t\t\t\t\t\t\t\tnewTable: values.table\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tcallback: function (r) {\n\t\t\t\t\t\t\t\t\tfrm.trigger('clear');\n\t\t\t\t\t\t\t\t\t// Reload the page\n\t\t\t\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\td.hide();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\td.show();\n\t\t\t\t}\n\n\t\t\t});\n\t\t})\n\t},\n\n\tcaptain_transfer: function (frm) {\n\t\tlet invoice = frm.doc.last_invoice\n\t\tif (invoice) {\n\t\t\tfrappe.call({\n\t\t\t\tmethod: 'frappe.client.get',\n\t\t\t\targs: {\n\t\t\t\t\tdoctype: 'POS Profile',\n\t\t\t\t\tname: frm.doc.pos_profile\n\t\t\t\t},\n\t\t\t\tcallback: function (response) {\n\t\t\t\t\tvar transfer_roles = response.message.transfer_role_permissions.map(role => role.role);\n\t\t\t\t\tvar user_roles = frappe.user_roles\n\t\t\t\t\tvar has_access = transfer_roles.some(role => user_roles.includes(role));\n\t\t\t\t\tif (has_access) {\n\t\t\t\t\t\tfrm.add_custom_button(__('Captain Transfer'), () => {\n\t\t\t\t\t\t\tfrappe.db.get_doc('POS Invoice', invoice).then(pos_invoice => {\n\t\t\t\t\t\t\t\tif (pos_invoice.invoice_printed === 1) {\n\t\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\t\ttitle: __(\"Invoice Already Billed\"),\n\t\t\t\t\t\t\t\t\t\tmessage: __(\"This order has already been billed. Please reload the page.\"),\n\t\t\t\t\t\t\t\t\t\tindicator: 'red'\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tlet d = new frappe.ui.Dialog({\n\t\t\t\t\t\t\t\t\t\ttitle: 'Transfer Captain',\n\t\t\t\t\t\t\t\t\t\tfields: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: 'New Captain',\n\t\t\t\t\t\t\t\t\t\t\t\tfieldname: 'captain',\n\t\t\t\t\t\t\t\t\t\t\t\tfieldtype: 'Link',\n\t\t\t\t\t\t\t\t\t\t\t\toptions: 'User',\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: 'Current Captain',\n\t\t\t\t\t\t\t\t\t\t\t\tfieldname: 'cur_captain',\n\t\t\t\t\t\t\t\t\t\t\t\tfieldtype: 'Data',\n\t\t\t\t\t\t\t\t\t\t\t\tread_only: true,\n\t\t\t\t\t\t\t\t\t\t\t\tdefault: pos_invoice.waiter\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tprimary_action_label: 'Transfer',\n\t\t\t\t\t\t\t\t\t\tprimary_action(values) {\n\t\t\t\t\t\t\t\t\t\t\tfrappe.dom.freeze();\n\t\t\t\t\t\t\t\t\t\t\tfrm.call({\n\t\t\t\t\t\t\t\t\t\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.captain_transfer',\n\t\t\t\t\t\t\t\t\t\t\t\targs: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tinvoice: frm.doc.last_invoice,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentCaptain: frm.doc.waiter,\n\t\t\t\t\t\t\t\t\t\t\t\t\tnewCaptain: values.captain\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\tcallback: function (r) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tfrm.trigger('clear');\n\t\t\t\t\t\t\t\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t\t\t\t\t\t\t\t\tdocument.addEventListener('click', function () {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\td.hide();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\td.show();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t},\n\n\tcancel_order: function (frm) {\n\t\tfrappe.call({\n\t\t\tmethod: 'ury.ury.api.button_permission.cancel_check',\n\t\t\tcallback: function (r) {\n\t\t\t\tif (r.message == true) {\n\t\t\t\t\tfrm.add_custom_button(__('Cancel'), () => {\n\t\t\t\t\t\tfrappe.db.get_doc('POS Invoice', frm.doc.last_invoice).then((pos_invoice) => {\n\t\t\t\t\t\t\tif (pos_invoice.invoice_printed === 1) {\n\t\t\t\t\t\t\t\tfrappe.throw({\n\t\t\t\t\t\t\t\t\ttitle: __(\"Invoice Already Billed\"),\n\t\t\t\t\t\t\t\t\tmessage: __(\"Not allowed to cancel billed orders.\"),\n\t\t\t\t\t\t\t\t\tindicator: 'red'\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tlet cancelFlag = false;\n\n\t\t\t\t\t\t\t\tvar dialog = new frappe.ui.Dialog({\n\t\t\t\t\t\t\t\t\ttitle: __(\"Confirm Cancellation\"),\n\t\t\t\t\t\t\t\t\tfields: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tfieldname: 'reason',\n\t\t\t\t\t\t\t\t\t\t\tfieldtype: 'Data',\n\t\t\t\t\t\t\t\t\t\t\tlabel: __('Reason'),\n\t\t\t\t\t\t\t\t\t\t\treqd: 1\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\tprimary_action: function () {\n\t\t\t\t\t\t\t\t\t\tvar reason = dialog.get_value('reason');\n\t\t\t\t\t\t\t\t\t\tif (!cancelFlag) {\n\t\t\t\t\t\t\t\t\t\t\tcancelFlag = true;\n\t\t\t\t\t\t\t\t\t\t\tfrm.reason = reason;\n\t\t\t\t\t\t\t\t\t\t\tfrm.cancel_reason = reason;\n\t\t\t\t\t\t\t\t\t\t\tfrm.trigger('cancel');\n\t\t\t\t\t\t\t\t\t\t\tdialog.hide();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tprimary_action_label: __('Cancel'),\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tdialog.show();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t});\n\t\t\t\t\t}).addClass(\"cancel-btn\");\n\t\t\t\t}\n\n\t\t\t}\n\t\t})\n\n\t},\n\n\tcancel: function (frm) {\n\t\tfrm.call({\n\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.cancel_order',\n\t\t\targs: {\n\t\t\t\tinvoice_id: frm.doc.last_invoice,\n\t\t\t\treason: frm.cancel_reason\n\t\t\t},\n\t\t\tcallback: function (r) {\n\t\t\t\tfrappe.show_alert({ message: __('Cancelled'), indicator: 'red' });\n\t\t\t\tsetTimeout(function () {\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t}, 1000)\n\t\t\t}\n\t\t});\n\n\t},\n\n\tfavourite: function (frm) {\n\t\tlet customerFavItems = '';\n\n\t\t$('#fav_items').empty();\n\n\t\tif (frm.doc.customer_name) {\n\t\t\tfrappe.call({\n\t\t\t\tmethod: 'ury.ury.doctype.ury_order.ury_order.customer_favourite_item',\n\t\t\t\targs: {\n\t\t\t\t\tcustomer_name: frm.doc.customer_name\n\t\t\t\t},\n\t\t\t\tcallback: function (r) {\n\t\t\t\t\tr.message.map((x) => {\n\t\t\t\t\t\tcustomerFavItems = `\n\t\t\t\t\t\t\t<div class=\"col-6 py-3\" style=\"padding: 0 15px; width: 250px;\">\n\t\t\t\t\t\t\t\t${x[\"item_name\"]}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"col-3 py-3\">\n\t\t\t\t\t\t\t\t${x[\"qty\"]}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t`;\n\n\t\t\t\t\t\tif (!$('#fav_items').html().includes(customerFavItems)) {\n\t\t\t\t\t\t\t// Check if the item is not already in the HTML content\n\t\t\t\t\t\t\t$('#fav_items').append(customerFavItems);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n});\n\n"
  },
  {
    "path": "ury/ury/doctype/ury_order/ury_order.json",
    "content": "{\n \"actions\": [],\n \"creation\": \"2023-09-13 23:49:03.431246\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"table_tab\",\n  \"take_away\",\n  \"table_list\",\n  \"take_away_list\",\n  \"restaurant_table\",\n  \"menu_tab\",\n  \"item_search\",\n  \"column_break_10\",\n  \"all_item\",\n  \"priority_item\",\n  \"section_break_13\",\n  \"item\",\n  \"customer_tab\",\n  \"customer_name\",\n  \"no_of_pax\",\n  \"favorite_item_section\",\n  \"favorite_items\",\n  \"order_tab\",\n  \"add_item\",\n  \"cart_items\",\n  \"grand_total\",\n  \"last_invoice\",\n  \"additional_details\",\n  \"items\",\n  \"waiter\",\n  \"pos_profile\",\n  \"cashier\",\n  \"comments\",\n  \"modified_time\",\n  \"column_break_3\",\n  \"current_order\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"table_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Table\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"take_away\",\n   \"fieldtype\": \"Check\",\n   \"hidden\": 1,\n   \"label\": \"Take Away\"\n  },\n  {\n   \"fieldname\": \"table_list\",\n   \"fieldtype\": \"HTML\",\n   \"label\": \"Table List\"\n  },\n  {\n   \"fieldname\": \"take_away_list\",\n   \"fieldtype\": \"HTML\",\n   \"label\": \"Take Away List\"\n  },\n  {\n   \"fieldname\": \"restaurant_table\",\n   \"fieldtype\": \"Link\",\n   \"hidden\": 1,\n   \"label\": \"Restaurant Table\",\n   \"options\": \"URY Table\"\n  },\n  {\n   \"fieldname\": \"menu_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Menu\"\n  },\n  {\n   \"fieldname\": \"item_search\",\n   \"fieldtype\": \"Data\"\n  },\n  {\n   \"fieldname\": \"column_break_10\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"all_item\",\n   \"fieldtype\": \"Button\",\n   \"label\": \"All\"\n  },\n  {\n   \"fieldname\": \"priority_item\",\n   \"fieldtype\": \"Button\",\n   \"label\": \"Priority\"\n  },\n  {\n   \"fieldname\": \"section_break_13\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"fieldname\": \"item\",\n   \"fieldtype\": \"HTML\",\n   \"label\": \"Item\",\n   \"options\": \"<div class=\\\"container px-0\\\">\\n<div class=\\\"row\\\" id=\\\"restaurant_menu_items\\\"></div>\\n</div>\"\n  },\n  {\n   \"fieldname\": \"customer_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Customer\"\n  },\n  {\n   \"fieldname\": \"customer_name\",\n   \"fieldtype\": \"Link\",\n   \"in_filter\": 1,\n   \"in_global_search\": 1,\n   \"in_standard_filter\": 1,\n   \"label\": \"Customer Name\",\n   \"options\": \"Customer\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"no_of_pax\",\n   \"fieldtype\": \"Int\",\n   \"in_list_view\": 1,\n   \"label\": \"Pax\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"favorite_item_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Favorite Item\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"favorite_items\",\n   \"fieldtype\": \"HTML\",\n   \"label\": \"Favorite Items\",\n   \"options\": \"<div class=\\\"container px-0\\\">\\n<div class=\\\"row\\\" id=\\\"fav_items\\\"></div>\\n</div>\"\n  },\n  {\n   \"fieldname\": \"order_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Order\"\n  },\n  {\n   \"depends_on\": \"eval:doc.restaurant_table || doc.take_away\",\n   \"description\": \"Click Enter To Add\",\n   \"fieldname\": \"add_item\",\n   \"fieldtype\": \"Link\",\n   \"hidden\": 1,\n   \"label\": \"Add Item\",\n   \"options\": \"Item\"\n  },\n  {\n   \"fieldname\": \"cart_items\",\n   \"fieldtype\": \"HTML\",\n   \"label\": \"Cart Items\",\n   \"options\": \"<div class=\\\"container px-0\\\">\\n<div id=\\\"restaurantCartItems\\\"></div>\\n</div>\"\n  },\n  {\n   \"fieldname\": \"grand_total\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Grand Total\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"last_invoice\",\n   \"fieldtype\": \"Link\",\n   \"hidden\": 1,\n   \"label\": \"Invoice\",\n   \"options\": \"POS Invoice\",\n   \"read_only\": 1\n  },\n  {\n   \"collapsible\": 1,\n   \"fieldname\": \"additional_details\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Additional Details\"\n  },\n  {\n   \"depends_on\": \"eval:doc.restaurant_table || doc.take_away\",\n   \"fieldname\": \"items\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Items\",\n   \"options\": \"URY Order Item\"\n  },\n  {\n   \"fieldname\": \"waiter\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Waiter\",\n   \"options\": \"User\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"pos_profile\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"POS Profile\",\n   \"options\": \"POS Profile\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"cashier\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Cashier\",\n   \"options\": \"User\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"comments\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Comments\"\n  },\n  {\n   \"fieldname\": \"column_break_3\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"depends_on\": \"eval:doc.restaurant_table || doc.take_away\",\n   \"fieldname\": \"current_order\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Current Order\"\n  },\n  {\n   \"fieldname\": \"modified_time\",\n   \"fieldtype\": \"Datetime\",\n   \"label\": \"Modified time\"\n  }\n ],\n \"hide_toolbar\": 1,\n \"issingle\": 1,\n \"links\": [],\n \"modified\": \"2024-01-10 19:37:37.858495\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Order\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"role\": \"System Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"role\": \"URY Captain\",\n   \"write\": 1\n  }\n ],\n \"quick_entry\": 1,\n \"search_fields\": \"customer_name\",\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_order/ury_order.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\nimport json\nimport frappe\nfrom frappe import _\nfrom frappe.model.document import Document\nfrom erpnext.controllers.queries import item_query\nfrom ury.ury_pos.api import getBranch, getBranchRoom\nfrom ury.ury.api.ury_kot_generate import kot_execute\nfrom ury.ury.api.ury_kot_generate import process_items_for_cancel_kot\n\nfrom frappe import cache\n\n\nclass URYOrder(Document):\n    pass\n\n\n@frappe.whitelist()\ndef get_order_invoice(table=None, invoiceNo=None, order_type=None, is_payment=None):\n    \"\"\"returns the active invoice linked to the given table\"\"\"\n\n    if table:\n        if is_payment == \"Payments\":\n            invoice_name = frappe.get_value(\n                \"POS Invoice\", dict(restaurant_table=table, docstatus=0, name=invoiceNo)\n            )\n            \n        else:\n            if invoiceNo:\n                invoice_name = frappe.get_value(\n                    \"POS Invoice\",\n                    dict(restaurant_table=table, docstatus=0, name=invoiceNo),\n                )\n               \n            else:\n                invoice_name = frappe.get_value(\n                    \"POS Invoice\",\n                    dict(restaurant_table=table, docstatus=0, invoice_printed=0),\n                )\n                \n        # invoice_name = frappe.get_value(\"POS Invoice\", dict(restaurant_table=table, docstatus=0, invoice_printed=0))\n        branch, menu_name, restaurant = get_restaurant_and_menu_name(table)\n\n        if invoice_name:\n            invoice = frappe.get_doc(\"POS Invoice\", invoice_name)\n\n        else:\n            invoice = frappe.new_doc(\"POS Invoice\")\n\n            invoice.naming_series = frappe.db.get_value(\n                \"URY Restaurant\", restaurant, \"invoice_series_prefix\"\n            )\n\n            invoice.is_pos = 1\n            invoice.update_stock = 1\n            invoice.restaurant = restaurant\n            invoice.branch = branch\n\n            is_take_away = frappe.db.get_value(\"URY Table\", table, \"is_take_away\")\n            if is_take_away == 1:\n                invoice.order_type = \"Take Away\"\n            else:\n                invoice.order_type= \"Dine In\"\n\n        invoice.taxes_and_charges = frappe.db.get_value(\n            \"URY Restaurant\", restaurant, \"default_tax_template\"\n        )\n\n        invoice.selling_price_list = frappe.db.get_value(\n            \"Price List\", dict(restaurant_menu=menu_name, enabled=1)\n        )\n\n    else:\n\n        if is_payment == \"Payments\":\n            invoice_name = frappe.get_value(\n                \"POS Invoice\", dict(restaurant_table=table, docstatus=0, name=invoiceNo)\n            )\n            \n        else:\n            invoice_name = frappe.get_value(\n                \"POS Invoice\", dict(docstatus=0, name=invoiceNo)\n            )\n            \n        if invoice_name:\n            invoice = frappe.get_doc(\"POS Invoice\", invoice_name)\n            \n\n        else:\n            invoice = frappe.new_doc(\"POS Invoice\")\n            invoice.is_pos = 1\n            invoice.update_stock = 1\n        \n        branch = getBranch()\n        restaurant = frappe.db.get_value(\"URY Restaurant\", {\"branch\": branch}, \"name\")\n   \n        menu=get_menu_name(order_type)\n \n        if (order_type == \"Aggregators\" and frappe.db.get_value(\"Branch\", branch, \"custom_no_taxes\") == 0) or order_type != \"Aggregators\":\n            invoice.taxes_and_charges = frappe.db.get_value(\"URY Restaurant\", restaurant, \"default_tax_template\")\n        \n        invoice.selling_price_list = frappe.db.get_value(\n            \"Price List\", dict(restaurant_menu=menu, enabled=1)\n        )\n        \n        \n\n    return invoice\n\n\n@frappe.whitelist()\ndef sync_order(\n    items,\n    cashier,\n    owner,\n    mode_of_payment,\n    customer,\n    no_of_pax,\n    last_invoice,\n    waiter,\n    pos_profile,\n    last_modified_time=None,\n    table=None,\n    invoice=None,\n    comments=None,\n    order_type=None,\n    aggregator_id=None,\n    room=None\n):\n    \n    user_role = frappe.get_roles()\n    posprofile = frappe.get_doc(\"POS Profile\", pos_profile)\n    \n    billing_user = any(\n        role.role in user_role for role in posprofile.role_allowed_for_billing\n    )\n\n    # Check if the last invoice was already billed\n    if (\n        last_invoice\n        and frappe.db.get_value(\"POS Invoice\", last_invoice, \"invoice_printed\") == 1\n        and (not billing_user)\n    ):\n        frappe.msgprint(\n            title=\"Invoice Already Billed\",\n            indicator=\"red\",\n            msg=(\"This order has already been billed. Please reload the page.\"),\n        )\n        return {\"status\": \"Failure\"}\n\n    invoice = get_order_invoice(table, invoice,order_type)\n\n    if last_invoice and last_modified_time:\n        lastModifiedTime = invoice.modified\n        from datetime import datetime\n\n        if isinstance(last_modified_time, str):\n            try:\n                last_modified_time = datetime.strptime(\n                    last_modified_time, \"%Y-%m-%d %H:%M:%S.%f\"\n                )\n            except ValueError:\n                last_modified_time = datetime.strptime(\n                    last_modified_time, \"%Y-%m-%d %H:%M:%S\"\n                )\n        if isinstance(lastModifiedTime, str):\n            try:\n                lastModifiedTime = datetime.strptime(\n                    lastModifiedTime, \"%Y-%m-%d %H:%M:%S.%f\"\n                )\n            except ValueError:\n                lastModifiedTime = datetime.strptime(\n                    lastModifiedTime, \"%Y-%m-%d %H:%M:%S\"\n                )\n        if lastModifiedTime != last_modified_time:\n            frappe.msgprint(\n                title=\"Order has been modified\",\n                indicator=\"red\",\n                msg=(\n                    \"This order has been modified. Please reload the page to retrieve the latest edits.\"\n                ),\n            )\n            return {\"status\": \"Failure\"}\n    else:\n        if invoice.name and invoice.invoice_printed == 0 and not billing_user:\n            frappe.msgprint(\n                title=\"Table occupied \",\n                indicator=\"red\",\n                msg=(\"{0} is already occupied . Please refresh the page.\").format(\n                    table\n                ),\n            )\n            return {\"status\": \"Failure\"}\n\n    if not customer:\n        frappe.throw(\"Please enter valid customer details\")\n    else:\n        invoice.customer = customer\n\n    if order_type:\n        invoice.order_type = order_type\n\n    customerdoc = frappe.get_doc(\"Customer\", customer)\n    invoice.mobile_number = customerdoc.mobile_number\n    if comments:\n        invoice.custom_comments = comments\n    invoice.no_of_pax = no_of_pax\n    invoice.pos_profile = pos_profile\n    invoice.cashier = cashier\n    invoice.waiter = waiter\n    invoice.custom_aggregator_id = aggregator_id\n    invoice.custom_restaurant_room =room\n    invoice.restaurant_table = table\n    \n    if order_type == \"Aggregators\":\n        price_list = frappe.db.get_value(\"Aggregator Settings\",{\"customer\": customer, \"parent\": invoice.branch, \"parenttype\": \"Branch\"},\"price_list\",)\n        \n        if not price_list:\n            frappe.throw(f\"Price list for customer {customer} in branch {invoice.branch} not found in Aggregator Settings.\")\n    else:\n        price_list = invoice.selling_price_list\n\n    # dummy payment\n    if invoice.invoice_created == 0:\n        invoice.append(\n            \"payments\",\n            dict(mode_of_payment=mode_of_payment, amount=invoice.grand_total),\n        )\n        invoice.invoice_created = 1\n\n    past_item = []\n    for item in invoice.items:\n        previous_item = {\n            \"item_code\": item.item_code,\n            \"item_name\": item.item_name,\n            \"qty\": item.qty,\n            \"comments\": \"\",\n        }\n        past_item.append(previous_item)\n        \n\n    # Conditional checking for 'items' type:\n    # - 'ury': JSON passed, hence using isinstance\n    # - 'ury_pos': Already formatted list, hence using else\n    if isinstance(items, str):\n        items = json.loads(items)\n    invoice.items = []\n    \n    menu = frappe.db.get_value(\"URY Menu\", {\"branch\": invoice.branch}, \"name\")\n   \n    for d in items:\n        \n        course = frappe.db.get_value(\"URY Menu Item\", {\"item\": d.get(\"item\"),\"parent\":menu}, \"course\")\n        \n        item_prices = frappe.db.get_list(\n            \"Item Price\",\n            filters={\"item_code\": d.get(\"item\"), \"price_list\": price_list},\n            fields=[\"price_list_rate\"],\n        )\n\n        if not item_prices:\n            frappe.throw(_(\"No item price found for Item: {0} in Price List: {1}. Please check the price list settings.\").format(d.get(\"item\"), price_list))\n\n        else:\n            invoice.append(\n                \"items\",\n                dict(\n                    item_code=d.get(\"item\"),\n                    item_name=d.get(\"item_name\"),\n                    qty=d.get(\"qty\"),\n                    **({\"custom_course\": course} if course else {}),\n                    comment=d.get(\"comment\"),\n                    rate = item_prices[0].price_list_rate,\n                    price_list_rate = item_prices[0].price_list_rate,\n                    base_price_list_rate = item_prices[0].price_list_rate,\n                    cost_center = frappe.db.get_value(\n                        \"POS Profile\", pos_profile, \"cost_center\"\n                        ),\n                ),\n            )\n\n    try:\n        invoice.save()\n    except Exception as e:\n        frappe.throw(f\"Error while updating order: {e}\")   \n\n\n    try:\n        kot_execute(invoice.name, customer, table, items, past_item, comments)\n\n    except Exception as e:\n        # If an exception occurs (e.g., \"kot\" app not found), it will be caught here without affect the code execution.\n        error_msg = f\"KOT Creation Failes {str(e)}\"            \n        frappe.log_error(error_msg, \"KOT Error\")\n\n    # table status\n    if invoice.invoice_printed == 0:\n        frappe.db.set_value(\n            \"URY Table\", table, {\"occupied\": 1, \"latest_invoice_time\": invoice.creation}\n        )\n\n    invoice.db_set(\"owner\", owner)\n    return invoice.as_dict()\n\n\n@frappe.whitelist()\ndef item_query_restaurant(\n    doctype=\"Item\",\n    txt=\"\",\n    searchfield=\"name\",\n    start=0,\n    page_len=20,\n    filters=None,\n    as_dict=False,\n):\n    \"\"\"Return items that are selected in active menu of the restaurant\"\"\"\n    restaurant, menu = get_restaurant_and_menu_name(filters[\"table\"])\n    items = frappe.db.get_all(\"URY Menu Item\", [\"item\"], dict(parent=menu, disabled=0))\n    del filters[\"table\"]\n    filters[\"name\"] = (\"in\", [d.item for d in items])\n\n    return item_query(\"Item\", txt, searchfield, start, page_len, filters, as_dict)\n\n\n@frappe.whitelist()\ndef get_restaurant_and_menu_name(table):\n    if not table:\n        frappe.throw(_(\"Please select a table\"))\n\n    restaurant, branch, room = frappe.get_value(\n        \"URY Table\",\n        table,\n        [\"restaurant\", \"branch\", \"restaurant_room\"],\n    )\n    room_wise_menu = frappe.db.get_value(\n        \"URY Restaurant\",\n        restaurant,\n        \"room_wise_menu\",\n    )\n\n    if not room_wise_menu:\n        menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n    else:\n        menu = frappe.db.get_value(\n            \"Menu for Room\",\n            {\"parent\": restaurant, \"room\": room},\n            \"menu\",\n        )\n\n    if not menu:\n        frappe.throw(\n            _(\"Please set an active menu for Restaurant {0}\").format(restaurant)\n        )\n\n    return branch, menu, restaurant\n\n@frappe.whitelist()\ndef get_menu_name(order_type):\n    branch = getBranch()\n    restaurant = frappe.get_value(\n        \"URY Restaurant\",\n        {\"branch\": branch},\n        \"name\",\n    )\n    order_type_wise_menu = frappe.db.get_value(\n            \"URY Restaurant\", restaurant, \"order_type_wise_menu\"\n        )\n    \n    if order_type_wise_menu:\n        menu = frappe.db.get_value(\n            \"Order Type Menu\",\n            {\"parent\": restaurant, \"order_type\": order_type},\n            \"menu\"\n        )\n        if not menu:\n            menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n    else:\n        menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")   \n    return menu  \n    \n\n@frappe.whitelist()\ndef pos_opening_check():\n    \n    user = frappe.session.user\n    # Handle the administrator case differently\n    if user == \"Administrator\":\n        return {\n            \"opening_exists\": False,  # Assuming no POS opening entry is needed for Administrator\n            \"cashier\": None,\n            \"pos_profile\": None,\n        }\n    \n    details = getBranchRoom()\n    room = details[0].get('name')    # 'Beach'\n    branch = details[0].get('branch') # 'Beach'\n    \n    pos_opening_list = frappe.db.sql(\"\"\"\n        SELECT DISTINCT `tabPOS Opening Entry`.name \n        FROM `tabPOS Opening Entry`\n        INNER JOIN `tabMultiple Rooms` \n        ON `tabMultiple Rooms`.parent = `tabPOS Opening Entry`.name\n        WHERE `tabPOS Opening Entry`.branch = %s\n        AND `tabPOS Opening Entry`.status = 'Open'\n        AND `tabPOS Opening Entry`.docstatus = 1\n        AND `tabMultiple Rooms`.room = %s\n    \"\"\", (branch, room), as_dict=True)\n    \n    \n    result = {\n        \"opening_exists\": len(pos_opening_list) > 0,\n        \"cashier\": None,\n        \"pos_profile\": None,\n    }\n\n    if result[\"opening_exists\"]:\n        # If POS opening entry exists, fetch the cashier from the first entry\n        opening_entry = frappe.get_doc(\"POS Opening Entry\", pos_opening_list[0].name)\n        result[\"cashier\"] = (\n            opening_entry.user\n        )  # Fetch values from POS Profile linked to POS Opening Entry\n        result[\"pos_profile\"] = opening_entry.pos_profile\n        \n    return result\n\n\n@frappe.whitelist()\ndef table_transfer(table, newTable, invoice):\n    current_table = frappe.get_doc(\"URY Table\", table)\n    pos_invoice = frappe.get_doc(\"POS Invoice\", invoice)\n    new_table = frappe.get_doc(\"URY Table\", newTable)\n\n    if current_table.restaurant_room == new_table.restaurant_room:\n        if new_table.occupied == 1:\n            frappe.throw(f\"Table {new_table.name} is already occupied\")\n\n        # Update table status\n        frappe.db.set_value(\n            \"URY Table\",\n            new_table.name,\n            {\"occupied\": 1, \"latest_invoice_time\": pos_invoice.creation},\n        )\n        frappe.db.set_value(\n            \"URY Table\",\n            current_table.name,\n            {\"occupied\": 0, \"latest_invoice_time\": None},\n        )\n\n        # Update POS Invoice\n        pos_invoice.restaurant_table = new_table.name\n        pos_invoice.save()\n\n        try:\n            change_table_in_kot(\n                    pos_invoice.name, new_table.name, pos_invoice.branch\n                )\n\n        except Exception as e:\n            # If an exception occurs (e.g., \"kot\" app not found), it will be caught here without effecting execution\n            pass\n\n    else:\n        frappe.throw(_(\"Table transfer between different rooms is restricted.\"))\n\n\n@frappe.whitelist()\ndef captain_transfer(currentCaptain, newCaptain, invoice):\n    pos_profile=frappe.get_value(\"POS Invoice\", invoice,\"pos_profile\")\n    multiple_cashier = frappe.db.get_value(\"POS Profile\",pos_profile,\"custom_enable_multiple_cashier\")\n    branch=frappe.get_value(\"POS Invoice\", invoice,\"branch\")\n    if multiple_cashier:\n        table=pos_profile=frappe.get_value(\"POS Invoice\", invoice,\"restaurant_table\")\n        current_room = frappe.get_value(\"URY Table\", table,\"restaurant_room\")\n        new_captain_room =  frappe.db.sql(\"\"\"\n                SELECT room\n                FROM `tabURY User`\n                WHERE parent=%s AND user=%s         \n            \"\"\",(branch,newCaptain),as_dict=True)\n        room_match = any(room['room'] == current_room for room in new_captain_room)\n        if not room_match:\n            frappe.throw(_(\"Captain transfer is not allowed between different rooms\"))\n        else:\n            current_captain_doc = frappe.get_doc(\"User\", currentCaptain)\n            pos_invoice = frappe.get_doc(\"POS Invoice\", invoice)\n            new_captain_doc = frappe.get_doc(\"User\", newCaptain)\n\n            # Update the waiter field of the POS Invoice\n            pos_invoice.waiter = new_captain_doc.name\n            pos_invoice.save()\n\n    else:\n        current_captain_doc = frappe.get_doc(\"User\", currentCaptain)\n        pos_invoice = frappe.get_doc(\"POS Invoice\", invoice)\n        new_captain_doc = frappe.get_doc(\"User\", newCaptain)\n\n        # Update the waiter field of the POS Invoice\n        pos_invoice.waiter = new_captain_doc.name\n        pos_invoice.save()\n\n\n@frappe.whitelist()\ndef customer_favourite_item(customer_name):\n    pos = frappe.db.get_list(\n        \"POS Invoice\", filters={\"customer\": customer_name}, fields=[\"name\"]\n    )\n\n    item_qty = {}\n\n    for invoice in pos:\n        pos_invoice = frappe.get_doc(\"POS Invoice\", invoice)\n        for item in pos_invoice.items:\n            item_name = item.item_name\n            item_qty[item_name] = item_qty.get(item_name, 0) + item.qty\n\n    result = [\n        {\"item_name\": item_name, \"qty\": qty}\n        for item_name, qty in item_qty.items()\n        if qty > 1\n    ]\n    result = sorted(result, key=lambda x: x[\"qty\"], reverse=True)[:3]\n\n    return result\n\n\n@frappe.whitelist()\ndef cancel_order(invoice_id, reason):\n    pos_invoice = frappe.get_doc(\"POS Invoice\", invoice_id)\n\n    # Update table status\n    frappe.db.set_value(\n        \"URY Table\",\n        pos_invoice.restaurant_table,\n        {\"occupied\": 0, \"latest_invoice_time\": None},\n    )\n\n    try:\n        cancel_kot(invoice_id)\n\n    except Exception as e:\n        # If an exception occurs (e.g., \"kot\" app not found), it will be caught here without effecting execution\n        pass\n\n    # Update invoice status\n    frappe.db.sql(\"\"\"\n        UPDATE `tabPOS Invoice Item`\n        SET docstatus = 2\n        WHERE parent = %s\n    \"\"\", (invoice_id,))\n\n    frappe.db.set_value(\"POS Invoice\", invoice_id, \"docstatus\", 2)\n    frappe.db.set_value(\"POS Invoice\", invoice_id, \"status\", \"Cancelled\")\n    frappe.db.set_value(\"POS Invoice\", invoice_id, \"cancel_reason\", reason)\n\n# Method for URY POS\n@frappe.whitelist()\ndef make_invoice(customer, payments, cashier, pos_profile,owner, additionalDiscount=None, table=None, invoice=None):\n    order_type =  invoice_name = frappe.get_value(\"POS Invoice\",invoice , \"order_type\")\n    invoice = get_order_invoice(table, invoice, order_type, \"Payments\")\n\n    if table:\n        restaurant = get_restaurant_and_menu_name(table)\n        invoice.restaurant = restaurant\n\n    invoice.customer = customer\n    invoice.pos_profile = pos_profile\n    invoice.additional_discount_percentage=additionalDiscount\n    invoice.calculate_taxes_and_totals()\n\n    for pay in invoice.payments:\n        pay.delete(pay.mode_of_payment)\n\n    for d in payments:\n        invoice.append(\n            \"payments\", dict(mode_of_payment=d[\"mode_of_payment\"], amount=d[\"amount\"])\n        )\n\n    # invoice.owner = owner\n    invoice.save()\n    try:\n        invoice.submit()\n    except Exception as e:\n        frappe.throw(f\"Error while settling order: {e}\")\n    \n    \n\n# Cancel KOT Doc Creation\ndef cancel_kot(invoice_id):\n\n    pos_invoice = frappe.get_doc(\"POS Invoice\", invoice_id)\n    pos_profile_id = pos_invoice.pos_profile\n    pos_profile = frappe.get_doc(\"POS Profile\", pos_profile_id)\n    kot_naming_series = pos_profile.custom_kot_naming_series\n    cancel_kot_naming_series = \"CNCL-\" + kot_naming_series\n\n    items = []\n    # Create a list of items for the canceled KOT\n    for item in pos_invoice.items:\n        order_item = {\n            \"item_code\": item.get(\"item\", item.get(\"item_code\")),\n            \"qty\": item.qty,\n            \"item_name\": item.item_name,\n        }\n        items.append(order_item)\n\n    if pos_invoice.restaurant_table:\n        restaurant_table = pos_invoice.restaurant_table\n    else:\n        restaurant_table = None\n\n    # Process items for a canceled KOT\n    process_items_for_cancel_kot(\n        invoice_id,\n        pos_invoice.customer,\n        restaurant_table,\n        items,\n        \"\",\n        pos_profile_id,\n        cancel_kot_naming_series,\n        \"Cancelled\",\n        items,\n    )\n\n    # Set the KOTs associated with the invoice as canceled\n    kot_list = frappe.db.get_list(\n        \"URY KOT\",\n        filters={\n            \"invoice\": invoice_id,\n            \"type\": (\"in\", (\"New Order\", \"Order Modified\")),\n            \"docstatus\": 1,\n        },\n        fields=(\"*\"),\n    )\n\n    for item in kot_list:\n        kot_doc = frappe.get_doc(\"URY KOT\", item.name)\n        kot_doc.docstatus = 2\n        kot_doc.save()\n\n\ndef change_table_in_kot(invoice, new_table, branch):\n    # Get a list of KOTs associated with the POS Invoice\n    kot_list = frappe.get_all(\n        \"URY KOT\",\n        filters={\n            \"invoice\": invoice,\n            \"docstatus\": 1,\n            \"order_status\": \"Ready For Prepare\",\n            \"verified\": 0,\n        },\n    )\n\n    # Update each KOT's restaurant_table and send a real-time update\n    for kot in kot_list:\n        frappe.db.set_value(\"URY KOT\", kot.name, \"restaurant_table\", new_table)\n        production = frappe.db.get_value(\"URY KOT\", kot.name, \"production\")\n        kot_channel = \"{}_{}_{}\".format(\"kot_update\", branch, production)\n        frappe.publish_realtime(kot_channel)\n"
  },
  {
    "path": "ury/ury/doctype/ury_order_item/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_order_item/test_ury_order_item.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYOrderItem(FrappeTestCase):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_order_item/ury_order_item.js",
    "content": "//  Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n//  For license information, please see license.txt\n\n\nfrappe.ui.form.on('URY Order Item', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_order_item/ury_order_item.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-09-13 23:46:44.326959\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item\",\n  \"item_name\",\n  \"qty\",\n  \"rate\",\n  \"comments\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"item\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item\",\n   \"options\": \"Item\",\n   \"read_only\": 1,\n   \"reqd\": 1\n  },\n  {\n   \"fetch_from\": \"item.item_name\",\n   \"fieldname\": \"item_name\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Item Name\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"qty\",\n   \"fieldtype\": \"Int\",\n   \"in_standard_filter\": 1,\n   \"label\": \"Qty\"\n  },\n  {\n   \"fieldname\": \"rate\",\n   \"fieldtype\": \"Currency\",\n   \"hidden\": 1,\n   \"in_list_view\": 1,\n   \"label\": \"Rate\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"comments\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Comments\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-12-16 16:16:02.465270\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Order Item\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_order_item/ury_order_item.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYOrderItem(Document):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_p_and_l_breakup/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_p_and_l_breakup/ury_p_and_l_breakup.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-10-05 15:35:53.488494\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"breakup\",\n  \"amount\",\n  \"percent\"\n ],\n \"fields\": [\n  {\n   \"columns\": 2,\n   \"fieldname\": \"breakup\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Breakup\",\n   \"reqd\": 1\n  },\n  {\n   \"columns\": 2,\n   \"fieldname\": \"amount\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Amount\",\n   \"precision\": \"2\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"percent\",\n   \"fieldtype\": \"Percent\",\n   \"label\": \"Percent\",\n   \"read_only\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-10-06 18:48:52.491943\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY P and L Breakup\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_p_and_l_breakup/ury_p_and_l_breakup.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYPandLBreakup(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_p_and_l_materials/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_p_and_l_materials/ury_p_and_l_materials.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-10-06 10:45:57.191236\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"material\",\n  \"cost_per_unit\",\n  \"units_consumed\",\n  \"amount\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"cost_per_unit\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Cost Per Unit\",\n   \"precision\": \"2\",\n   \"read_only\": 1,\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"units_consumed\",\n   \"fieldtype\": \"Float\",\n   \"in_list_view\": 1,\n   \"label\": \"Units Consumed\",\n   \"precision\": \"2\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"amount\",\n   \"fieldtype\": \"Currency\",\n   \"in_list_view\": 1,\n   \"label\": \"Amount\",\n   \"options\": \"currency\",\n   \"read_only\": 1,\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"material\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Material\",\n   \"read_only\": 1,\n   \"reqd\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-10-06 11:51:11.653896\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY P and L Materials\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_p_and_l_materials/ury_p_and_l_materials.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYPandLMaterials(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_printer_settings/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_printer_settings/ury_printer_settings.json",
    "content": "{\n \"actions\": [],\n \"autoname\": \"autoincrement\",\n \"creation\": \"2023-09-14 11:56:08.017313\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"bill\",\n  \"printer\"\n ],\n \"fields\": [\n  {\n   \"default\": \"1\",\n   \"fieldname\": \"bill\",\n   \"fieldtype\": \"Check\",\n   \"in_list_view\": 1,\n   \"label\": \"Bill\"\n  },\n  {\n   \"fieldname\": \"printer\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Printer\",\n   \"options\": \"Network Printer Settings\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-09-28 23:37:08.879383\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Printer Settings\",\n \"naming_rule\": \"Autoincrement\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_printer_settings/ury_printer_settings.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYPrinterSettings(Document):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_production_item_groups/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_production_item_groups/ury_production_item_groups.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-09-27 12:17:38.689372\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"item_group\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"item_group\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Item Group\",\n   \"options\": \"Item Group\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-09-27 12:17:38.689372\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Production Item Groups\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_production_item_groups/ury_production_item_groups.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYProductionItemGroups(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_production_unit/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_production_unit/test_ury_production_unit.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYProductionUnit(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_production_unit/ury_production_unit.js",
    "content": "// Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY Production Unit', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_production_unit/ury_production_unit.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"autoname\": \"field:production\",\n \"creation\": \"2023-09-27 12:18:07.130899\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"production\",\n  \"pos_profile\",\n  \"branch\",\n  \"warehouse\",\n  \"item_groups\",\n  \"printer_info_section\",\n  \"printer_settings\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"production\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Production\",\n   \"unique\": 1\n  },\n  {\n   \"fieldname\": \"pos_profile\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"POS Profile\",\n   \"options\": \"POS Profile\"\n  },\n  {\n   \"fetch_from\": \"pos_profile.branch\",\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"read_only\": 1\n  },\n  {\n   \"fetch_from\": \"pos_profile.warehouse\",\n   \"fieldname\": \"warehouse\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Warehouse\",\n   \"options\": \"Warehouse\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"item_groups\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Item Groups\",\n   \"options\": \"URY Production Item Groups\"\n  },\n  {\n   \"collapsible\": 1,\n   \"fieldname\": \"printer_info_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Printer info\"\n  },\n  {\n   \"fieldname\": \"printer_settings\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Printers\",\n   \"options\": \"URY Printer Settings\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"links\": [],\n \"modified\": \"2023-10-04 18:52:21.594992\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Production Unit\",\n \"naming_rule\": \"By fieldname\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1,\n   \"share\": 1\n  },\n  {\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"share\": 1\n  }\n ],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_production_unit/ury_production_unit.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYProductionUnit(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_report_settings/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_report_settings/test_ury_report_settings.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and Contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYReportSettings(FrappeTestCase):\n\tpass\n"
  },
  {
    "path": "ury/ury/doctype/ury_report_settings/ury_report_settings.js",
    "content": "// Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n// For license information, please see license.txt\n\nfrappe.ui.form.on('URY Report Settings', {\n\trefresh: function(frm) {\n        frm.page.wrapper.find(\".comment-box\").css({'display':'none'});\n\t\tfrm.set_query(\"buying_price_list\", function() {\n            return {\n                \"filters\": {\n                    \"buying\":1\n                }\n            }\n        });\n        if(frm.doc.__islocal){\n            frm.clear_table('consumables');\n            frm.add_child('consumables', {\n                material:\"Gas\",\n                cost_per_unit: 0.0\n            });\n            frm.add_child('consumables', {\n                material:\"Charcoal\",\n                cost_per_unit: 0.0\n            });\n            frm.refresh_field('consumables');\n        }\n\t}\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_report_settings/ury_report_settings.json",
    "content": "{\n \"actions\": [],\n \"autoname\": \"field:branch\",\n \"creation\": \"2023-09-29 10:45:19.994545\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"branch\",\n  \"extended_hours\",\n  \"hours\",\n  \"daily_p_and_l\",\n  \"buying_price_list\",\n  \"direct_expense_section\",\n  \"consumables\",\n  \"direct_fixed_expenses\",\n  \"indirect_expense_section\",\n  \"electricity_charges\",\n  \"indirect_fixed_expenses\",\n  \"monthly_fixed_expenses\",\n  \"percentage_expenses\",\n  \"employee_costs_section\",\n  \"employee_costs\",\n  \"section_break_4c2bz\",\n  \"depreciation\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"reqd\": 1,\n   \"unique\": 1\n  },\n  {\n   \"default\": \"0\",\n   \"description\": \"Whether the branch is open after 12 AM.\",\n   \"fieldname\": \"extended_hours\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Extended Hours\",\n   \"reqd\": 1\n  },\n  {\n   \"depends_on\": \"eval:doc.extended_hours==1\",\n   \"fieldname\": \"hours\",\n   \"fieldtype\": \"Int\",\n   \"label\": \"No Of Hours\",\n   \"mandatory_depends_on\": \"eval:doc.extended_hours==1\",\n   \"non_negative\": 1\n  },\n  {\n   \"fieldname\": \"daily_p_and_l\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Daily P and L Settings\"\n  },\n  {\n   \"fieldname\": \"buying_price_list\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Buying Price List\",\n   \"options\": \"Price List\",\n   \"reqd\": 1\n  },\n  {\n   \"collapsible\": 1,\n   \"fieldname\": \"direct_expense_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Direct Expenses\"\n  },\n  {\n   \"description\": \"Daily Fixed\",\n   \"fieldname\": \"direct_fixed_expenses\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Direct Fixed Expenses\",\n   \"options\": \"URY Fixed Expenses\"\n  },\n  {\n   \"collapsible\": 1,\n   \"fieldname\": \"indirect_expense_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Indirect Expenses\",\n   \"options\": \"Staff Food Charges\"\n  },\n  {\n   \"description\": \"Daily Fixed\",\n   \"fieldname\": \"indirect_fixed_expenses\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Indirect Fixed Expenses\",\n   \"options\": \"URY Fixed Expenses\"\n  },\n  {\n   \"fieldname\": \"percentage_expenses\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Percentage Expenses\",\n   \"options\": \"URY Variable Expenses\"\n  },\n  {\n   \"description\": \"Daily Fixed\",\n   \"fieldname\": \"depreciation\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Depreciation\",\n   \"precision\": \"2\"\n  },\n  {\n   \"collapsible\": 1,\n   \"description\": \"<li>Daily Gross Salary Cost is calculated from employees attendance.</li>\\n<br>\",\n   \"fieldname\": \"employee_costs_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Employee Costs\"\n  },\n  {\n   \"fieldname\": \"section_break_4c2bz\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"description\": \"Per Unit\",\n   \"fieldname\": \"electricity_charges\",\n   \"fieldtype\": \"Currency\",\n   \"label\": \"Electricty Charges\"\n  },\n  {\n   \"fieldname\": \"consumables\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Burning Materials (Other Consumables)\",\n   \"options\": \"URY Materials\"\n  },\n  {\n   \"description\": \"Daily Fixed\",\n   \"fieldname\": \"employee_costs\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Employee Costs\",\n   \"options\": \"URY Fixed Expenses\"\n  },\n  {\n   \"description\": \"Monthly Fixed\",\n   \"fieldname\": \"monthly_fixed_expenses\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Monthly Fixed Expenses\",\n   \"options\": \"URY Fixed Expenses\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"links\": [],\n \"modified\": \"2024-03-22 15:34:04.572515\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Report Settings\",\n \"naming_rule\": \"By fieldname\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  }\n ],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_report_settings/ury_report_settings.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\nimport frappe\nfrom frappe.model.document import Document\n\nclass URYReportSettings(Document):\n\tdef validate(self):\n\t\tif self.extended_hours ==1 and self.hours == 0:\n\t\t\tfrappe.throw(msg=('Value cannot be zero for URY Report Settings: <strong>No Of Hours<strong>'), title=(\"Zero Value\"))\n"
  },
  {
    "path": "ury/ury/doctype/ury_restaurant/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_restaurant/test_ury_restaurant.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYRestaurant(FrappeTestCase):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_restaurant/ury_restaurant.js",
    "content": "//  Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n//  For license information, please see license.txt\n\nfrappe.ui.form.on('URY Restaurant', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_restaurant/ury_restaurant.json",
    "content": "{\n \"actions\": [],\n \"autoname\": \"prompt\",\n \"creation\": \"2023-09-13 23:41:41.934914\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"document_type\": \"Setup\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"image\",\n  \"company\",\n  \"invoice_series_prefix\",\n  \"aggregator_series_prefix\",\n  \"column_break_4\",\n  \"address\",\n  \"branch\",\n  \"default_tax_template\",\n  \"menu_info_section\",\n  \"active_menu\",\n  \"room_wise_menu\",\n  \"menu_for_room\",\n  \"column_break_vo5jt\",\n  \"default_room\",\n  \"order_type_wise_menu\",\n  \"order_type_menu\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"image\",\n   \"fieldtype\": \"Attach Image\",\n   \"hidden\": 1,\n   \"label\": \"Image\",\n   \"print_hide\": 1\n  },\n  {\n   \"fieldname\": \"company\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Company\",\n   \"options\": \"Company\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"invoice_series_prefix\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Invoice Series Prefix\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"column_break_4\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"active_menu\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Default Menu\",\n   \"options\": \"URY Menu\"\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"default_tax_template\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Default Tax Template\",\n   \"options\": \"Sales Taxes and Charges Template\"\n  },\n  {\n   \"fieldname\": \"address\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Address\",\n   \"options\": \"Address\"\n  },\n  {\n   \"fieldname\": \"menu_info_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Menu Info\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"room_wise_menu\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Room Wise Menu\"\n  },\n  {\n   \"depends_on\": \"eval:doc.room_wise_menu \",\n   \"fieldname\": \"menu_for_room\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Menu For Room\",\n   \"options\": \"Menu for Room\"\n  },\n  {\n   \"fieldname\": \"column_break_vo5jt\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"default_room\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Default Room\",\n   \"options\": \"URY Room\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"aggregator_series_prefix\",\n   \"fieldtype\": \"Data\",\n   \"label\": \"Aggregator Series Prefix\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"order_type_wise_menu\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Order Type Wise Menu\"\n  },\n  {\n   \"depends_on\": \"eval:doc.order_type_wise_menu \",\n   \"fieldname\": \"order_type_menu\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Order Type Menu\",\n   \"options\": \"Order Type Menu\"\n  }\n ],\n \"image_field\": \"image\",\n \"links\": [],\n \"modified\": \"2025-03-06 10:39:17.307700\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Restaurant\",\n \"naming_rule\": \"Set by user\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"select\": 1,\n   \"set_user_permissions\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1\n  },\n  {\n   \"read\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"write\": 1\n  }\n ],\n \"quick_entry\": 1,\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": [],\n \"track_changes\": 1\n}"
  },
  {
    "path": "ury/ury/doctype/ury_restaurant/ury_restaurant.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYRestaurant(Document):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_room/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_room/test_ury_room.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYRoom(FrappeTestCase):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_room/ury_room.js",
    "content": "//  Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n//  For license information, please see license.txt\n\nfrappe.ui.form.on('URY Room', {\n\t// refresh: function(frm) {\n\n\t// }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_room/ury_room.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"autoname\": \"Prompt\",\n \"creation\": \"2023-09-13 23:44:37.688874\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"branch\",\n  \"column_break_ahfni\",\n  \"room_type\",\n  \"section_break_hrqbe\",\n  \"printer_settings\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"room_type\",\n   \"fieldtype\": \"Select\",\n   \"label\": \"Room Type\",\n   \"options\": \"AC\\nNON-AC\"\n  },\n  {\n   \"fieldname\": \"printer_settings\",\n   \"fieldtype\": \"Table\",\n   \"label\": \"Printer Settings\",\n   \"options\": \"URY Printer Settings\"\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"column_break_ahfni\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"section_break_hrqbe\",\n   \"fieldtype\": \"Section Break\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"links\": [],\n \"modified\": \"2024-05-01 19:54:10.412708\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Room\",\n \"naming_rule\": \"Set by user\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1\n  },\n  {\n   \"print\": 1,\n   \"read\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"write\": 1\n  }\n ],\n \"quick_entry\": 1,\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_room/ury_room.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYRoom(Document):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_table/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_table/test_ury_table.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# See license.txt\n\n# import frappe\nfrom frappe.tests.utils import FrappeTestCase\n\n\nclass TestURYTable(FrappeTestCase):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_table/ury_table.js",
    "content": "//  Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n//  For license information, please see license.txt\n\n// frappe.ui.form.on('URY Table', {\n// \t// refresh: function(frm) {\n\n// \t// }\n// });\n\nfrappe.ui.form.on('URY Table', {\n    refresh(frm) {\n        frm.set_query('restaurant_room', function () {\n            if (!frm.doc.branch) {\n                return {\n                    filters: {\n                        name: ['=', '']\n                    }\n                };\n            }\n            return {\n                filters: {\n                    branch: frm.doc.branch\n                }\n            };\n        });\n    },\n\n    branch(frm) {\n        // Clear room when restaurant changes\n        frm.set_value('restaurant_room', null);\n    }\n});\n"
  },
  {
    "path": "ury/ury/doctype/ury_table/ury_table.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"autoname\": \"prompt\",\n \"creation\": \"2023-09-13 23:44:01.744181\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"table_info_tab\",\n  \"no_of_seats\",\n  \"minimum_seating\",\n  \"table_shape\",\n  \"column_break_vub4k\",\n  \"restaurant\",\n  \"restaurant_room\",\n  \"branch\",\n  \"layout_position_section\",\n  \"layout_x\",\n  \"layout_width\",\n  \"column_break_olsi\",\n  \"layout_y\",\n  \"layout_height\",\n  \"section_break_mcm3o\",\n  \"is_take_away\",\n  \"active_info_tab\",\n  \"occupied\",\n  \"column_break_280tb\",\n  \"latest_invoice_time\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"table_info_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Table Info\"\n  },\n  {\n   \"fieldname\": \"no_of_seats\",\n   \"fieldtype\": \"Int\",\n   \"label\": \"No of Seats\"\n  },\n  {\n   \"fieldname\": \"minimum_seating\",\n   \"fieldtype\": \"Int\",\n   \"in_list_view\": 1,\n   \"label\": \"Minimum Seating\"\n  },\n  {\n   \"fieldname\": \"column_break_vub4k\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"restaurant\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Restaurant\",\n   \"options\": \"URY Restaurant\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"restaurant_room\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Restaurant Room\",\n   \"options\": \"URY Room\",\n   \"reqd\": 1\n  },\n  {\n   \"fetch_from\": \"restaurant.branch\",\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"options\": \"Branch\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"section_break_mcm3o\",\n   \"fieldtype\": \"Section Break\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"is_take_away\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Is Take Away\"\n  },\n  {\n   \"fieldname\": \"active_info_tab\",\n   \"fieldtype\": \"Tab Break\",\n   \"label\": \"Active Info\"\n  },\n  {\n   \"default\": \"0\",\n   \"fieldname\": \"occupied\",\n   \"fieldtype\": \"Check\",\n   \"label\": \"Occupied\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"column_break_280tb\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"default\": \"Now\",\n   \"fieldname\": \"latest_invoice_time\",\n   \"fieldtype\": \"Time\",\n   \"label\": \"Latest Invoice Time\",\n   \"read_only\": 1\n  },\n  {\n   \"fieldname\": \"table_shape\",\n   \"fieldtype\": \"Select\",\n   \"label\": \"Table Shape\",\n   \"options\": \"\\nRectangle\\nSquare\\nCircle\"\n  },\n  {\n   \"fieldname\": \"layout_position_section\",\n   \"fieldtype\": \"Section Break\",\n   \"label\": \"Layout Position\"\n  },\n  {\n   \"fieldname\": \"layout_x\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Layout X\"\n  },\n  {\n   \"fieldname\": \"layout_width\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Layout Width\"\n  },\n  {\n   \"fieldname\": \"column_break_olsi\",\n   \"fieldtype\": \"Column Break\"\n  },\n  {\n   \"fieldname\": \"layout_y\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Layout Y\"\n  },\n  {\n   \"fieldname\": \"layout_height\",\n   \"fieldtype\": \"Float\",\n   \"label\": \"Layout Height\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"links\": [],\n \"modified\": \"2025-12-31 15:45:13.235634\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Table\",\n \"naming_rule\": \"Set by user\",\n \"owner\": \"Administrator\",\n \"permissions\": [\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"System Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"email\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Captain\",\n   \"select\": 1\n  },\n  {\n   \"create\": 1,\n   \"delete\": 1,\n   \"email\": 1,\n   \"export\": 1,\n   \"print\": 1,\n   \"read\": 1,\n   \"report\": 1,\n   \"role\": \"URY Manager\",\n   \"select\": 1,\n   \"share\": 1,\n   \"write\": 1\n  },\n  {\n   \"print\": 1,\n   \"read\": 1,\n   \"role\": \"URY Cashier\",\n   \"select\": 1,\n   \"write\": 1\n  }\n ],\n \"row_format\": \"Dynamic\",\n \"rows_threshold_for_grid_search\": 20,\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": [],\n \"track_changes\": 1\n}"
  },
  {
    "path": "ury/ury/doctype/ury_table/ury_table.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYTable(Document):\n    def autoname(self):\n        prefix = re.sub(\"-+\", \"-\", self.restaurant.replace(\" \", \"-\"))\n        self.name = make_autoname(prefix + \"-.##\")\n"
  },
  {
    "path": "ury/ury/doctype/ury_user/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_user/ury_user.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-09-13 23:50:07.542900\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"user\",\n  \"room\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"user\",\n   \"fieldtype\": \"Link\",\n   \"in_list_view\": 1,\n   \"label\": \"User\",\n   \"options\": \"User\"\n  },\n  {\n   \"fieldname\": \"room\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Room\",\n   \"options\": \"URY Room\"\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2024-08-19 18:30:54.832691\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY User\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_user/ury_user.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\n\nclass URYUser(Document):\n    pass\n"
  },
  {
    "path": "ury/ury/doctype/ury_variable_expenses/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/doctype/ury_variable_expenses/ury_variable_expenses.json",
    "content": "{\n \"actions\": [],\n \"allow_rename\": 1,\n \"creation\": \"2023-10-04 14:54:32.483102\",\n \"default_view\": \"List\",\n \"doctype\": \"DocType\",\n \"editable_grid\": 1,\n \"engine\": \"InnoDB\",\n \"field_order\": [\n  \"expense\",\n  \"percentage_type\",\n  \"percent\"\n ],\n \"fields\": [\n  {\n   \"fieldname\": \"expense\",\n   \"fieldtype\": \"Data\",\n   \"in_list_view\": 1,\n   \"label\": \"Expense\",\n   \"reqd\": 1\n  },\n  {\n   \"fieldname\": \"percentage_type\",\n   \"fieldtype\": \"Select\",\n   \"in_list_view\": 1,\n   \"label\": \"Percentage Type\",\n   \"options\": \"Gross Sales\\nNet Sales\",\n   \"reqd\": 1\n  },\n  {\n   \"description\": \"% of selected sales\",\n   \"fieldname\": \"percent\",\n   \"fieldtype\": \"Percent\",\n   \"in_list_view\": 1,\n   \"label\": \"Percent\",\n   \"reqd\": 1\n  }\n ],\n \"index_web_pages_for_search\": 1,\n \"istable\": 1,\n \"links\": [],\n \"modified\": \"2023-10-05 17:25:47.313238\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY Variable Expenses\",\n \"owner\": \"Administrator\",\n \"permissions\": [],\n \"sort_field\": \"modified\",\n \"sort_order\": \"DESC\",\n \"states\": []\n}"
  },
  {
    "path": "ury/ury/doctype/ury_variable_expenses/ury_variable_expenses.py",
    "content": "# Copyright (c) 2023, Tridz Technologies Pvt. Ltd. and contributors\n# For license information, please see license.txt\n\n# import frappe\nfrom frappe.model.document import Document\n\nclass URYVariableExpenses(Document):\n\tpass\n"
  },
  {
    "path": "ury/ury/hooks/ury_item.py",
    "content": "import frappe\n\n\ndef validate(doc,method):\n    update_menu_item(doc,method)\n    update_variants_add_on(doc, method)\n    \n    \ndef update_menu_item(doc, event):\n    menu_items = frappe.get_all('URY Menu Item', filters={'item': doc.item_code})\n    for menu_item in menu_items:\n        frappe.db.set_value('URY Menu Item', menu_item.name, 'item_name', doc.item_name)\n\ndef update_variants_add_on(doc, event):\n    if doc.custom_pos_add_on_items:\n        for row in doc.custom_pos_add_on_items:\n            if not frappe.db.exists(\"URY Menu Item\", {\"item\": row.item}):\n                frappe.throw(f\"Item '{row.item}' in POS Add On Items is not in URY Menu\")\n\n    if doc.custom_pos_item_variants:\n        for row in doc.custom_pos_item_variants:\n            if not frappe.db.exists(\"URY Menu Item\", {\"item\": row.item}):\n                frappe.throw(f\"Item '{row.item}' in POS Item Variants is not in URY Menu\")\n"
  },
  {
    "path": "ury/ury/hooks/ury_pos_closing_entry.py",
    "content": "import frappe\n\ndef before_save(doc, method):\n    sub_pos_close_check(doc, method)\n\ndef validate(doc, method):\n    calculate_closing_amount(doc, method)\n    validate_cashier(doc, method)\n\n\ndef sub_pos_close_check(doc,method):\n    cashier = None\n    multiple_cashier = frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"custom_enable_multiple_cashier\")\n    if multiple_cashier:\n        get_cashier = frappe.get_doc(\"POS Profile\", doc.pos_profile)\n        for user_details in get_cashier.applicable_for_users:\n            if not user_details.custom_main_cashier:\n                cashier = user_details.user\n        if frappe.session.user != cashier:\n            branch=frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"branch\")\n            pos_opening_list = frappe.get_all(\n                \"POS Opening Entry\",\n                fields=[\"name\", \"docstatus\", \"status\", \"posting_date\"],\n                filters={\"branch\": branch,\"user\":cashier},\n            )\n            flag = 0\n            for pos_opening in pos_opening_list:\n                if pos_opening.status == \"Open\" and pos_opening.docstatus == 1:\n                    flag = 1\n            if flag == 1:\n                frappe.throw((\"Sub Cashier POS  must be closed\"), title=(\"Sub Cashier POS Closing Required\"))\n                \n            return flag\n    else:\n        pass\n\ndef calculate_closing_amount(doc, method):\n    multiple_cashier = frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"custom_enable_multiple_cashier\")\n    if multiple_cashier:  \n        sub_pos_closing = frappe.get_all(\n            \"Sub POS Closing\",\n            filters=[\n                [\"posting_date\", \"<=\", doc.posting_date],\n                [\"period_start_date\", \">=\", doc.period_start_date],\n                [\"docstatus\", \"=\", 1]\n            ],\n            fields=[\"name\"] \n        )\n        if sub_pos_closing:\n            for closing_details in doc.payment_reconciliation:\n                sub_closing_amount = frappe.db.get_value(\"Sub POS Closing Payment\",{\"parent\":sub_pos_closing[0].name,\"mode_of_payment\":closing_details.mode_of_payment},\"closing_amount\") or 0\n                main_closing_amount = closing_details.custom_closing_amount or 0\n                total_closing_amount = sub_closing_amount + main_closing_amount\n                closing_details.closing_amount = total_closing_amount\n                closing_details.difference = total_closing_amount - closing_details.expected_amount\n        else:\n            frappe.throw(\"No Sub POS Closing entries found between the given dates\")\n            return None\n    else:\n        pass\ndef validate_cashier(doc, method):\n    cashier = None\n    multiple_cashier = frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"custom_enable_multiple_cashier\")\n    if multiple_cashier:\n        get_cashier = frappe.get_doc(\"POS Profile\", doc.pos_profile)\n        for user_details in get_cashier.applicable_for_users:\n            if not user_details.custom_main_cashier:\n                cashier = user_details.user\n        if frappe.session.user == cashier:\n            frappe.throw(\"Sub Cashiers are not allowed to make POS Closing Entries.\")\n    else:\n        pass\n    "
  },
  {
    "path": "ury/ury/hooks/ury_pos_invoice.py",
    "content": "import frappe\nfrom datetime import datetime\nfrom frappe.utils import now_datetime, get_time,now\n\n\ndef before_insert(doc, method):\n    pos_invoice_naming(doc, method)\n    order_type_update(doc, method)\n    restrict_existing_order(doc, method)\n\n\ndef validate(doc, method):\n    validate_invoice(doc, method)\n    validate_customer(doc, method)\n    validate_price_list(doc, method)\n\n\ndef before_submit(doc, method):\n    calculate_and_set_times(doc, method)\n    validate_invoice_print(doc, method)\n    ro_reload_submit(doc, method)\n\n\ndef on_trash(doc, method):\n    table_status_delete(doc, method)\n\n\ndef validate_invoice(doc, method):\n    if doc.waiter == None or doc.waiter == \"\":\n        doc.waiter = doc.modified_by\n    remove_items = frappe.db.get_value(\"POS Profile\", doc.pos_profile, \"remove_items\")\n    \n    if doc.invoice_printed == 1 and remove_items == 0:\n        # Get the original items from db\n        original_doc = frappe.get_doc(\"POS Invoice\", doc.name)\n        \n        # Create dictionaries to store both quantities and names\n        original_items = {\n            item.item_code: {\"qty\": item.qty, \"name\": item.item_name} \n            for item in original_doc.items\n        }\n        current_items = {\n            item.item_code: {\"qty\": item.qty, \"name\": item.item_name} \n            for item in doc.items\n        }\n          \n        # Check for removed items\n        removed_items = set(original_items.keys()) - set(current_items.keys())\n        \n        # Check for quantity reductions\n        reduced_qty_items = []\n        for item_code, item_data in original_items.items():\n            if (item_code in current_items and \n                current_items[item_code][\"qty\"] < item_data[\"qty\"]):\n                reduced_qty_items.append(\n                    f\"{item_data['name']} (qty reduced from {item_data['qty']} \"\n                    f\"to {current_items[item_code]['qty']})\"\n                )\n        \n        if removed_items or reduced_qty_items:\n            error_msg = []\n            if removed_items:\n                removed_item_names = [\n                    original_items[item_code][\"name\"] \n                    for item_code in removed_items\n                ]\n                error_msg.append(f\"Removed items: {', '.join(removed_item_names)}\")\n            if reduced_qty_items:\n                error_msg.append(f\"Modified quantities: {', '.join(reduced_qty_items)}\")\n                \n            frappe.throw(\n                (\"Cannot modify items after invoice is printed.\\n{0}\")\n                .format(\"\\n\".join(error_msg))\n            )\n\n\ndef validate_customer(doc, method):\n    if doc.customer_name == None or doc.customer_name == \"\":\n        frappe.throw(\n            (\" Failed to load data , Please Refresh the page \").format(\n                doc.customer_name\n            )\n        )\n\n\ndef calculate_and_set_times(doc, method):\n    doc.arrived_time = doc.creation\n\n    current_time_str = now()\n    \n    current_time = datetime.strptime(current_time_str, \"%Y-%m-%d %H:%M:%S.%f\")\n    \n    time_difference = current_time - doc.creation\n    \n    total_seconds = int(time_difference.total_seconds())\n    hours, remainder = divmod(total_seconds, 3600)\n    minutes, seconds = divmod(remainder, 60)\n    \n    formatted_spend_time = f\"{hours:02d}:{minutes:02d}:{seconds:02d}\"\n    doc.total_spend_time = formatted_spend_time\n\n\ndef validate_invoice_print(doc, method):\n    # Check if the invoice has been printed\n    invoice_printed = frappe.db.get_value(\"POS Invoice\", doc.name, \"invoice_printed\")\n\n    # If the invoice is associated with a restaurant table and hasn't been printed\n    if doc.restaurant_table and invoice_printed == 0:\n        frappe.throw(\n            \"Printing the invoice is mandatory before submitting. Please print the invoice.\"\n        )\n\n\ndef table_status_delete(doc, method):\n    if doc.restaurant_table:\n        frappe.db.set_value(\n            \"URY Table\",\n            doc.restaurant_table,\n            {\"occupied\": 0, \"latest_invoice_time\": None},\n        )\n\n\ndef pos_invoice_naming(doc, method):\n    pos_profile = frappe.get_doc(\"POS Profile\", doc.pos_profile)\n    restaurant = pos_profile.restaurant\n\n    if not doc.restaurant_table:\n        doc.naming_series = frappe.db.get_value(\n            \"URY Restaurant\", restaurant, \"invoice_series_prefix\"\n        )\n        \n        if doc.order_type == \"Aggregators\":\n            doc.naming_series = frappe.db.get_value(\n                \"URY Restaurant\", restaurant, \"aggregator_series_prefix\"\n            )\n    \n\n\ndef order_type_update(doc, method):\n    if doc.restaurant_table:\n        if not doc.order_type:\n            is_take_away = frappe.db.get_value(\n                \"URY Table\", doc.restaurant_table, \"is_take_away\"\n            )\n            if is_take_away == 1:\n                doc.order_type = \"Take Away\"\n            else:\n                doc.order_type = \"Dine In\"\n    \n\n\n# reload restaurant order page if submitted invoice is open there\ndef ro_reload_submit(doc, method):\n    frappe.publish_realtime(\"reload_ro\", {\"name\": doc.name})\n\n\ndef validate_price_list(doc, method):\n        \n    if doc.restaurant:\n        \n        if doc.restaurant_table:\n            room = frappe.db.get_value(\"URY Table\", doc.restaurant_table, \"restaurant_room\")\n            menu_name = (\n                frappe.db.get_value(\"URY Restaurant\", doc.restaurant, \"active_menu\")\n                if not frappe.db.get_value(\n                    \"URY Restaurant\", doc.restaurant, \"room_wise_menu\"\n                )\n                else frappe.db.get_value(\n                    \"Menu for Room\", {\"parent\": doc.restaurant, \"room\": room}, \"menu\"\n                )\n            )\n\n            doc.selling_price_list = frappe.db.get_value(\n                \"Price List\", dict(restaurant_menu=menu_name, enabled=1)\n            )\n        \n        if doc.order_type == \"Aggregators\":\n            price_list = frappe.db.get_value(\"Aggregator Settings\",\n                {\"customer\": doc.customer, \"parent\": doc.branch, \"parenttype\": \"Branch\"},\n                \"price_list\",\n                )\n            \n            if not price_list:\n                frappe.throw(f\"Price list for customer {doc.customer} in branch {doc.branch} not found in Aggregator Settings.\")\n                \n            doc.selling_price_list = price_list\n            \n        else:\n            menu_name = frappe.db.get_value(\"URY Restaurant\", doc.restaurant, \"active_menu\") \n\n            doc.selling_price_list = frappe.db.get_value(\n                \"Price List\", dict(restaurant_menu=menu_name, enabled=1)\n            )\n            \n\ndef restrict_existing_order(doc, event):\n    if doc.restaurant_table:\n        invoice_exist = frappe.db.exists(\n            \"POS Invoice\",\n            {\n                \"restaurant_table\": doc.restaurant_table,\n                \"docstatus\": 0,\n                \"invoice_printed\": 0,\n            },\n        )\n        if invoice_exist:\n            frappe.throw(\n                (\"Table {0} has an existing invoice\").format(doc.restaurant_table)\n            )\n"
  },
  {
    "path": "ury/ury/hooks/ury_pos_opening_entry.py",
    "content": "import frappe\nfrom frappe.utils import today\nfrom frappe.utils import  get_datetime,today,now\n\ndef validate(doc,method):\n    set_cashier_room(doc,method)\n    \ndef before_save(doc, method):\n    main_pos_open_check(doc, method)\n    set_current_time(doc,method)\n    \n    \ndef set_cashier_room(doc,method):\n    room =  frappe.db.sql(\"\"\"\n                SELECT room , parent\n                FROM `tabURY User`\n                WHERE parent=%s AND user=%s         \n            \"\"\",(doc.branch,doc.user),as_dict=True)\n    \n    if room:\n        doc.custom_room = room[0]['room']\n        multiple_cashier = frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"custom_enable_multiple_cashier\")\n        if multiple_cashier:\n            doc.custom_rooms = []\n            for room in room:\n                doc.append('custom_rooms', {\n                    'room': room['room']\n                })\n\ndef set_current_time(doc,method):\n    multiple_cashier = frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"custom_enable_multiple_cashier\")\n    if multiple_cashier:\n        date_time = now()\n        doc.period_start_date = date_time\n    else:\n        pass\n\ndef main_pos_open_check(doc,method):\n    owner = None\n    current_date = today()\n    multiple_cashier = frappe.db.get_value(\"POS Profile\",doc.pos_profile,\"custom_enable_multiple_cashier\")\n    if multiple_cashier:\n        get_cashier = frappe.get_doc(\"POS Profile\", doc.pos_profile)\n        for user_details in get_cashier.applicable_for_users:\n            if user_details.custom_main_cashier:\n                owner = user_details.user\n\n        if frappe.session.user != owner:\n            pos_opening_list = frappe.get_all(\n                \"POS Opening Entry\",\n                fields=[\"name\", \"docstatus\", \"status\", \"posting_date\"],\n                filters={\"branch\": doc.branch,\"user\":owner,\"posting_date\":current_date},\n            )\n            flag = 1\n            for pos_opening in pos_opening_list:\n                if pos_opening.status == \"Open\" and pos_opening.docstatus == 1:\n                    flag = 0\n            if flag == 1:\n                frappe.throw((\"Main Cashier POS must be open\"), title=(\"Main Cashier POS Required\"))\n                \n            return flag\n    else:\n        pass\n"
  },
  {
    "path": "ury/ury/hooks/ury_pos_profile.py",
    "content": "import frappe\nfrom frappe import _, msgprint\n\n\ndef validate(doc, method):\n    validate_bill_check(doc, method)\n    validate_cost_center(doc, method)\n\n\ndef validate_bill_check(doc, method):\n    for row in doc.printer_settings:\n        if not row.bill or not row.printer:\n            msgprint(\n                _(\n                    \"Either Bill is not enabled / Printer is not selected in Printer Settings.\"\n                )\n            )\n            \ndef validate_cost_center(doc, method):\n    if not doc.cost_center:\n       frappe.throw(\n                _(\n                    \"Cost center is mandatory.\"\n                )\n            )\n"
  },
  {
    "path": "ury/ury/hooks/ury_sales_invoice.py",
    "content": "import frappe\n\n\ndef before_insert(doc, method):\n    sales_invoice_naming(doc, method)\n\ndef on_update(doc,method):\n    aggregator_unpaid(doc,method)\n    \ndef sales_invoice_naming(doc, method):\n    if not doc.is_pos:\n        return\n    \n    if not doc.pos_profile:\n        return\n    \n    pos_profile = frappe.db.get_value(\n        \"POS Profile\", \n        doc.pos_profile, \n        [\"restaurant_prefix\", \"restaurant\"], \n        as_dict=True\n    )\n\n    if not pos_profile:\n        frappe.throw(f\"POS Profile '{doc.pos_profile}' does not exist. Please select a valid POS Profile.\")\n    \n    restaurant = pos_profile.get(\"restaurant\")\n\n    if pos_profile.get(\"restaurant_prefix\") == 1 and restaurant:\n        if doc.order_type == \"Aggregators\":\n            \n            # Get the aggregator series prefix\n            aggregator_series_prefix = frappe.db.get_value(\n                \"URY Restaurant\", \n                restaurant, \n                \"aggregator_series_prefix\"\n            )\n            \n            if aggregator_series_prefix: \n                doc.naming_series = \"SINV-\" +  aggregator_series_prefix\n                \n            else: \n                # Fallback to invoice_series_prefix if aggregator_series_prefix is not available            \n                doc.naming_series = \"SINV-\" + frappe.db.get_value(\"URY Restaurant\", restaurant, \"invoice_series_prefix\")\n                      \n        else:\n            # Use invoice_series_prefix for non-aggregator orders\n            doc.naming_series = \"SINV-\" + frappe.db.get_value(\n                \"URY Restaurant\", restaurant, \"invoice_series_prefix\"\n            )\n            \n            \ndef aggregator_unpaid(doc,method):\n    if doc.order_type == \"Aggregators\" and frappe.db.get_value(\"Branch\", doc.branch , \"custom_make_unpaid\") == 1 :\n        doc.is_pos = 0\n        \n        \ndef remove_tax(doc,method):\n    \n    if doc.order_type == \"Aggregators\" and frappe.db.get_value(\"Branch\", doc.branch , \"custom_no_taxes\") == 1 :\n\n        doc.taxes_and_charges = None\n        \n        doc.taxes.clear()\n       # Manually adjust totals\n        # doc.total_taxes_and_charges = 0\n        # doc.grand_total = doc.base_grand_total = doc.net_total\n        # doc.outstanding_amount = doc.grand_total - doc.paid_amount\n        # doc.run_method(\"validate\")\n\n        \n\n"
  },
  {
    "path": "ury/ury/page/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/page/websocket_print/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/page/websocket_print/websocket_print.js",
    "content": "frappe.pages['websocket-print'].on_page_load = function (wrapper) {\n\tvar page = frappe.ui.make_app_page({\n\t\tparent: wrapper,\n\t\ttitle: 'Websocket Print',\n\t\tsingle_column: true\n\t});\n\tfrappe.call({\n\t\tmethod: 'ury.ury_pos.api.getBranch',\n\t\tcallback: function (r) {\n\t\t\tconst branch = r.message;\n\t\t\tconst print_channel = `print_${branch}`;\n\t\t\tfrappe.realtime.on(print_channel, (data) => {\n\t\t\t\tif (!(data.data.name in localStorage)) {\n\t\t\t\t\tget_print_html(set_preview, wrapper, data.data.doctype, data.data.name, data.data.print_format)\n\t\t\t\t\tlocalStorage.setItem(data.data.name, '')\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n}\n\nfrappe.pages['websocket-print'].refresh = function (wrapper) {\n}\n\nlet get_print_html = function (set_preview, wrapper, doc, name, print_format) {\n\tthis._req = frappe.call({\n\t\tmethod: \"frappe.www.printview.get_html_and_style\",\n\t\targs: {\n\t\t\tdoc: doc,\n\t\t\tname: name,\n\t\t\tprint_format: print_format,\n\t\t\t_lang: 'en',\n\t\t},\n\t\tcallback: function (r) {\n\t\t\tset_preview(r, wrapper);\n\t\t},\n\t});\n};\n\nconst set_preview = function (val = null, wrapper) {\n\tlet print_wrapper = $(wrapper).find('.layout-main-section');\n\tlet html = val.message.html;\n\t//create ifram\n\tconst iframe = document.createElement('iframe');\n\tiframe.id = 'print_content';\n\n\tdocument.body.appendChild(iframe)\n\n\tiframe.contentWindow.document.open()\n\tiframe.contentWindow.document.write(html)\n\tiframe.contentWindow.document.close()\n\n\t// print_wrapper.html(iframe)\n\tconsole.log(val.message.html)\n\tiframe.contentWindow.print();\n\tiframe.parentNode.removeChild(iframe);\n}"
  },
  {
    "path": "ury/ury/page/websocket_print/websocket_print.json",
    "content": "{\n \"content\": null,\n \"creation\": \"2023-10-03 12:50:54.725452\",\n \"docstatus\": 0,\n \"doctype\": \"Page\",\n \"idx\": 0,\n \"modified\": \"2023-10-03 12:50:54.725452\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"websocket-print\",\n \"owner\": \"Administrator\",\n \"page_name\": \"websocket-print\",\n \"roles\": [\n  {\n   \"role\": \"URY Captain\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  },\n  {\n   \"role\": \"System Manager\"\n  }\n ],\n \"script\": null,\n \"standard\": \"Yes\",\n \"style\": null,\n \"system_page\": 0,\n \"title\": \"Websocket Print\"\n}"
  },
  {
    "path": "ury/ury/report/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/average_bill_value/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/average_bill_value/average_bill_value.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:49:49.209829\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2023-12-06 10:44:45.501427\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Average Bill Value\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    date_list.`date` AS \\\"Date\\\",\\n    COUNT(b.name) AS \\\"Bill Nos:Link/POS Invoice\\\",\\n    ROUND(SUM(b.grand_total),2) AS \\\"Total Sales\\\",\\n    ROUND((SUM(b.grand_total)/count(b.name)),2) AS \\\"ABV\\\"\\n    \\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND b.`docstatus` = 1\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    date_list.`date`\\nORDER BY \\n    date_list.`date` DESC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Average Bill Value\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/cancelled_invoices/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/cancelled_invoices/cancelled_invoices.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:50:36.967491\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2024-01-08 11:12:47.938817\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Cancelled Invoices\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT \\n    b.`posting_date` AS \\\"Date\\\",\\n    CONCAT(\\n    LPAD(IF(HOUR(b.`posting_time`) > 12, HOUR(b.`posting_time`) - 12, HOUR(b.`posting_time`)), 2, '0'),\\n    ':',\\n    SUBSTRING_INDEX(SUBSTRING_INDEX(b.`posting_time`, ':', 2), ':', -1),\\n    CASE WHEN HOUR(b.`posting_time`) >= 12 THEN ' PM' ELSE ' AM' END\\n    ) AS \\\"Time\\\",\\n    b.`name` AS \\\"Invoice:Link/POS Invoice\\\",\\n    b.`modified_by` AS \\\"Cancelled By\\\",\\n    b.`cancel_reason` AS \\\"Cancellation Reason\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n       \\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`docstatus` = 2 \\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    date_list.`date`,b.`name`\\nORDER BY \\n    date_list.`date` ASC, b.`name` ASC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Cancelled Invoices\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/customer_data/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/customer_data/customer_data.json",
    "content": "{\n \"add_total_row\": 1,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:51:46.583438\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"customer\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Customer\",\n   \"mandatory\": 1,\n   \"options\": \"Customer\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2023-12-06 10:45:27.118901\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Customer Data\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"select\\n    b.`posting_date` AS \\\"Date\\\",\\n    b.name AS \\\"POS Invoice:Link/POS Invoice\\\",\\n    b.customer_name AS \\\"Customer Name\\\",\\n    b.mobile_number AS \\\"Mobile Number\\\",\\n    b.total AS \\\"Total Amount\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND b.`docstatus` = 1\\n    AND b.customer_name = %(customer)s\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    date_list.`date`,b.`name`\\nORDER BY \\n    date_list.`date` ASC, b.`name` ASC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Customer Data\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/daywise_customer_details/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/daywise_customer_details/daywise_customer_details.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2024-08-23 15:24:43.592459\",\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 6,\n \"is_standard\": \"Yes\",\n \"letterhead\": null,\n \"modified\": \"2024-09-08 13:42:35.264877\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Daywise Customer Details\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    b.customer AS \\\"Customer ID\\\",\\n    b.customer_name AS \\\"Customer Name\\\",\\n    b.mobile_number AS \\\"Mobile Number\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\", \\\"Paid\\\")\\n    AND b.`docstatus` = 1\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n)\\nGROUP BY\\n    b.customer\\nORDER BY\\n    b.customer_name;\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Daywise Customer Details\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"URY Manager\"\n  },\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/daywise_invoices/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/daywise_invoices/daywise_invoices.json",
    "content": "{\n \"add_total_row\": 1,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:47:56.403837\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"letterhead\": null,\n \"modified\": \"2024-12-22 12:50:12.080353\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Daywise Invoices\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT \\n    b.`posting_date` AS \\\"Date\\\",\\n    CONCAT(\\n    LPAD(IF(HOUR(b.`posting_time`) > 12, HOUR(b.`posting_time`) - 12, HOUR(b.`posting_time`)), 2, '0'),\\n    ':',\\n    SUBSTRING_INDEX(SUBSTRING_INDEX(b.`posting_time`, ':', 2), ':', -1),\\n    CASE WHEN HOUR(b.`posting_time`) >= 12 THEN ' PM' ELSE ' AM' END\\n    ) AS \\\"Time\\\",\\n    b.`name` AS \\\"Invoice:Link/POS Invoice\\\",\\n    b.`net_total` AS \\\"Item Total\\\",\\n    b.`total_taxes_and_charges` AS \\\"Total Taxes and Charges\\\",\\n    b.`grand_total` AS \\\"Grand Total\\\",\\n    (b.`grand_total` - b.`rounded_total`) AS \\\"Round Off\\\",\\n    b.`rounded_total` AS \\\"Rounded Total\\\",\\n    CASE WHEN b.`customer_group` = 'Aggregators' THEN 0 ELSE b.`paid_amount` END AS \\\"Received Amount\\\",\\n    CASE WHEN b.`customer_group` = 'Aggregators' THEN 0 ELSE b.`change_amount` END AS \\\"Change Amount\\\",\\n    IFNULL(\\n    CASE \\n        WHEN b.`rounded_total` > 0 THEN (b.`rounded_total` - b.`paid_amount` + b.`change_amount`)\\n        ELSE (b.`grand_total` - b.`paid_amount` + b.`change_amount`)\\n    END, \\n    0\\n    ) AS \\\"Cash Discounts\\\",\\n    GROUP_CONCAT(\\n        CASE WHEN c.`amount` != 0 THEN c.`mode_of_payment` END \\n        ORDER BY c.`amount` \\n        SEPARATOR ', '\\n    ) AS \\\"Payment Mode\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND b.`docstatus` = 1\\n)\\nLEFT JOIN `tabSales Invoice Payment` c ON (\\n    c.`parent`= b.`name`\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n    (\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n    )\\nGROUP BY \\n    date_list.`date`,b.`name`\\nORDER BY \\n    date_list.`date` ASC, b.`name` ASC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Daywise Invoices\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/daywise_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/daywise_sales/daywise_sales.json",
    "content": "{\n \"add_total_row\": 1,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:46:17.686947\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2023-12-06 10:43:35.653786\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Daywise Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT \\n    date_list.`date` AS \\\"Date\\\",\\n    COUNT(b.`name`) AS \\\"Total Invoices\\\",\\n    ROUND(SUM(b.`net_total`),2) AS \\\"Item Total\\\",\\n    ROUND(SUM(b.`total_taxes_and_charges`),2) AS \\\"Total Taxes and Charges\\\",\\n    ROUND(SUM(b.`grand_total`),2) AS \\\"Grand Total\\\",\\n    ROUND(SUM(b.`grand_total` - b.`rounded_total`),2) AS \\\"Round Off\\\",\\n    ROUND(SUM(b.`rounded_total` - b.`paid_amount` + b.`change_amount`), 2)AS \\\"Cash Discounts\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND b.`docstatus` = 1 \\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    date_list.`date`\\nORDER BY \\n    date_list.`date` DESC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Daywise Sales\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/employee_item_wise_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/employee_item_wise_sales/employee_item_wise_sales.json",
    "content": "{\n \"add_total_row\": 1,\n \"columns\": [],\n \"creation\": \"2024-01-05 16:55:21.262143\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"employee\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"User\",\n   \"mandatory\": 1,\n   \"options\": \"User\",\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2024-07-29 19:49:46.477463\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Employee Item Wise Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    b.`item_name` AS \\\"Item name\\\",\\n    i.`item_group` AS \\\"Item Group\\\",\\n    SUM(b.`qty`) AS \\\"Qty\\\",\\n    SUM(b.`amount`) AS \\\"Amount\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` a ON (\\n    a.`branch` = %(branch)s\\n    AND a.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND a.`docstatus` = 1\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nINNER JOIN `tabPOS Invoice Item` b ON (\\n\\ta.name = b.parent\\n)\\nINNER JOIN `tabUser` e ON (\\n    e.`name` = %(employee)s\\n    AND e.`name`= a.`waiter`\\n)\\nINNER JOIN `tabItem` i ON (\\n    b.`item_code` = i.`item_code`\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND a.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(a.`posting_date`, a.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(a.`posting_date`, a.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND a.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    b.`item_name`, i.`item_group`\\nORDER BY \\n    date_list.`date` DESC\\n\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Employee Item Wise Sales\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/employee_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/employee_sales/employee_sales.json",
    "content": "{\n \"add_total_row\": 1,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:57:00.807965\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2023-12-06 10:45:38.913296\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Employee Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n     e.`full_name` AS \\\"Employee\\\",\\n    date_list.`date` AS \\\"Date\\\",\\n    COUNT(b.`name`) AS \\\"Total Invoices\\\",\\n    SUM(b.`grand_total`) AS \\\"Sales Amount\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND b.`docstatus` = 1\\n)\\nINNER JOIN `tabUser` e ON(\\n    e.`name`= b.`waiter`\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    date_list.`date`,e.`full_name`    \\nORDER BY \\n    date_list.`date` DESC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Employee Sales\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/item_wise_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/item_wise_sales/item_wise_sales.json",
    "content": "{\n \"add_total_row\": 1,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:51:11.124653\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"options\": \"\",\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2023-12-06 10:45:13.122820\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Item Wise Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT \\n    c.`item_group` AS \\\"Item Group\\\",\\n    c.`item_name` AS \\\"Item Name\\\",\\n    SUM(b.`qty`) AS \\\"Qty\\\",\\n    SUM(b.`amount`) AS \\\"Amount\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` a ON (\\n    a.`branch` = %(branch)s\\n    AND a.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND a.`docstatus` = 1 \\n)\\nINNER JOIN `tabPOS Invoice Item`b ON a.`name`=b.`parent`\\nLEFT JOIN `tabItem` c ON c.`item_code` = b.`item_code`\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n(\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND a.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(a.`posting_date`, a.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(a.`posting_date`, a.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND a.`posting_date` = date_list.`date`)\\n)\\nGROUP BY \\n    c.`item_name`\\nORDER BY \\n    c.`item_group` ASC, b.`item_name` ASC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Item Wise Sales\",\n \"report_script\": \"\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/month_wise_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/month_wise_sales/month_wise_sales.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:48:49.987205\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2023-12-06 10:44:19.481381\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Month Wise Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    YEAR(daily_report.`Date`) AS \\\"YEAR\\\",\\n    MONTHNAME(daily_report.`Date`) AS \\\"MONTH\\\",\\n    ROUND(SUM(daily_report.`Item Total`), 2) AS \\\"Item Total\\\",\\n    ROUND(SUM(daily_report.`Total Taxes and Charges`),2) AS \\\"Total Taxes and Charges\\\",\\n    ROUND(SUM(daily_report.`Grand Total`), 2) AS \\\"Grand Total\\\"\\nFROM \\n    (\\n        SELECT \\n            date_list.`date` AS \\\"Date\\\",\\n            ROUND(SUM(b.`net_total`),2) AS \\\"Item Total\\\",\\n            ROUND(SUM(b.`total_taxes_and_charges`),2) AS \\\"Total Taxes and Charges\\\",\\n            ROUND(SUM(b.`grand_total`),2) AS \\\"Grand Total\\\"\\n        FROM \\n            (\\n                SELECT  DATE_SUB(DATE_FORMAT(CURDATE(), '%%Y-%%m-01'),INTERVAL 4 MONTH) AS `date`\\n                UNION\\n                SELECT DATE_ADD(DATE_SUB(DATE_FORMAT(CURDATE(), '%%Y-%%m-01'),INTERVAL 4 MONTH), INTERVAL n DAY) AS `date`\\n                FROM (\\n                    SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n                    FROM (\\n                        SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n                    ) AS a\\n                    CROSS JOIN (\\n                        SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n                    ) AS b\\n                    CROSS JOIN (\\n                        SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n                    ) AS c\\n                    ORDER BY n\\n                ) AS nums\\n                WHERE DATE_ADD(DATE_SUB(DATE_FORMAT(CURDATE(), '%%Y-%%m-01'),INTERVAL 4 MONTH), INTERVAL n DAY) <= CURDATE()\\n            ) AS date_list\\n        LEFT JOIN `tabPOS Invoice` b ON (\\n            b.`branch` = %(branch)s\\n            AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n            AND b.`docstatus` = 1 \\n        )\\n        LEFT JOIN `tabURY Report Settings` rs ON (\\n            rs.`branch` = %(branch)s\\n        )\\n        WHERE\\n        (\\n            ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n            OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n            OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n        )\\n        GROUP BY \\n            date_list.`date`\\n        ORDER BY \\n            date_list.`date` DESC\\n    ) AS daily_report\\nGROUP BY\\n    YEAR(daily_report.`Date`),\\n    MONTH(daily_report.`Date`)\\nORDER BY\\n    YEAR(daily_report.`Date`) DESC,\\n    MONTH(daily_report.`Date`) DESC;\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Month Wise Sales\",\n \"report_script\": \"\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/repeated_customers/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/repeated_customers/repeated_customers.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:52:16.514901\",\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"letterhead\": null,\n \"modified\": \"2024-08-24 12:08:25.676663\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Repeated Customers\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    `Date`,\\n    `Total Customers`,\\n    `New Customers`,\\n    `Total Customers`-`New Customers` AS `Repeated Customers`,\\n    ROUND(((`Total Customers`-`New Customers`) / `Total Customers`)*100,2)  AS Percentage\\nFROM\\n    (SELECT\\n    date_list.`date` AS `Date`,\\n    COUNT(d.customer) AS `Total Customers`,\\n    COUNT(DISTINCT CASE WHEN b.customer IS NOT NULL THEN b.customer END) AS `New Customers`\\n    FROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\n    LEFT JOIN `tabPOS Invoice` d ON (\\n        d.`branch` = %(branch)s\\n        AND d.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n        AND d.`docstatus` = 1\\n    )\\n    LEFT JOIN `tabPOS Invoice` b ON (\\n        b.`name` = d.`name`\\n        AND b.`branch` = %(branch)s\\n        AND b.`status` IN (\\\"Consolidated\\\", \\\"Paid\\\")\\n        AND b.`docstatus` = 1\\n        AND NOT EXISTS (\\n            SELECT 1\\n            FROM `tabPOS Invoice` c\\n            LEFT JOIN `tabURY Report Settings` rs ON (\\n                rs.`branch` = %(branch)s\\n            )\\n            WHERE\\n                (\\n                    ((rs.`hours` IS NULL OR rs.`hours` = 0) IS NULL AND c.`posting_date` < date_list.`date`)\\n                    OR (rs.`hours` > 0 AND TIMESTAMP(c.`posting_date`, c.`posting_time`) < TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n                    OR (rs.`branch` IS NULL AND c.`posting_date` < date_list.`date`)\\n                )\\n                AND c.`customer` = b.`customer`\\n                AND c.`branch` = %(branch)s\\n                AND c.`status` IN (\\\"Consolidated\\\", \\\"Paid\\\")\\n                AND c.`docstatus` = 1\\n        )\\n    )\\n    LEFT JOIN `tabURY Report Settings` rs ON (\\n        rs.`branch` = %(branch)s\\n    )\\n    WHERE\\n    (\\n        ((rs.`hours` IS NULL OR rs.`hours` = 0) IS NULL AND d.`posting_date` = date_list.`date`)\\n        OR (rs.`hours` > 0 AND TIMESTAMP(d.`posting_date`, d.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(d.`posting_date`, d.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n        OR (rs.`branch` IS NULL AND d.`posting_date` = date_list.`date`)\\n    )\\n    GROUP BY \\n        date_list.`date`\\n    ORDER BY \\n        date_list.`date` DESC) AS subquery;\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Repeated Customers\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/service_wise_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/service_wise_sales/service_wise_sales.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2024-01-05 15:55:03.793993\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"start_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"From Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"end_date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"To Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2024-01-05 15:55:03.793993\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Service Wise Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT \\n    b.`posting_date` AS \\\"Date\\\",\\n    b.`order_type` AS \\\"Order Type\\\",\\n    SUM(b.`grand_total`) AS \\\"Grand Total\\\"\\nFROM \\n    (\\n        SELECT %(start_date)s AS `date`\\n        UNION\\n        SELECT DATE_ADD(%(start_date)s, INTERVAL n DAY) AS `date`\\n        FROM (\\n            SELECT a.N + b.N * 10 + c.N * 100 + 1 AS n\\n            FROM (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS a\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS b\\n            CROSS JOIN (\\n                SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9\\n            ) AS c\\n            ORDER BY n\\n        ) AS nums\\n        WHERE DATE_ADD(%(start_date)s, INTERVAL n DAY) < %(end_date)s\\n        UNION\\n        SELECT %(end_date)s AS `date`\\n    ) AS date_list\\nLEFT JOIN `tabPOS Invoice` b ON (\\n    b.`branch` = %(branch)s\\n    AND b.`status` IN (\\\"Consolidated\\\",\\\"Paid\\\") \\n    AND b.`docstatus` = 1\\n)\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n    (\\n    ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = date_list.`date`)\\n    OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(date_list.`date`, INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(date_list.`date`, CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n    OR (rs.`branch` IS NULL AND b.`posting_date` = date_list.`date`)\\n    )\\nGROUP BY \\n    date_list.`date`, b.`order_type`\\nORDER BY \\n    date_list.`date` ASC, b.`order_type` ASC\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Service Wise Sales\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/time_wise_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/time_wise_sales/time_wise_sales.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2024-01-05 15:05:51.590275\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"date\",\n   \"fieldtype\": \"Date\",\n   \"label\": \"Date\",\n   \"mandatory\": 1,\n   \"wildcard_filter\": 0\n  },\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2024-01-05 15:14:38.448441\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Time Wise Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    time_intervals.`Time Interval`,\\n    COALESCE(SUM(sales_by_interval.`Sales`), 0) AS 'Sales',\\n    COUNT(sales_by_interval.`Bill`) AS 'Bills'\\nFROM (\\n    SELECT '12 AM - 02 AM' AS 'Time Interval', 1 AS 'Order'\\n    UNION ALL SELECT '02 AM - 04 AM', 2\\n    UNION ALL SELECT '04 AM - 06 AM', 3\\n    UNION ALL SELECT '06 AM - 08 AM', 4\\n    UNION ALL SELECT '08 AM - 10 AM', 5\\n    UNION ALL SELECT '10 AM - 12 PM', 6\\n    UNION ALL SELECT '12 PM - 02 PM', 7\\n    UNION ALL SELECT '02 PM - 04 PM', 8\\n    UNION ALL SELECT '04 PM - 06 PM', 9\\n    UNION ALL SELECT '06 PM - 08 PM', 10\\n    UNION ALL SELECT '08 PM - 10 PM', 11\\n    UNION ALL SELECT '10 PM - 12 AM', 12\\n) AS time_intervals\\nLEFT JOIN (\\n    SELECT\\n        CASE\\n            WHEN TIME(`posting_time`) BETWEEN '00:00:00' AND '01:59:59' THEN '12 AM - 02 AM'\\n            WHEN TIME(`posting_time`) BETWEEN '02:00:00' AND '03:59:59' THEN '02 AM - 04 AM'\\n            WHEN TIME(`posting_time`) BETWEEN '04:00:00' AND '05:59:59' THEN '04 AM - 06 AM'\\n            WHEN TIME(`posting_time`) BETWEEN '06:00:00' AND '07:59:59' THEN '06 AM - 08 AM'\\n            WHEN TIME(`posting_time`) BETWEEN '08:00:00' AND '09:59:59' THEN '08 AM - 10 AM'\\n            WHEN TIME(`posting_time`) BETWEEN '10:00:00' AND '11:59:59' THEN '10 AM - 12 PM'\\n            WHEN TIME(`posting_time`) BETWEEN '12:00:00' AND '13:59:59' THEN '12 PM - 02 PM'\\n            WHEN TIME(`posting_time`) BETWEEN '14:00:00' AND '15:59:59' THEN '02 PM - 04 PM'\\n            WHEN TIME(`posting_time`) BETWEEN '16:00:00' AND '17:59:59' THEN '04 PM - 06 PM'\\n            WHEN TIME(`posting_time`) BETWEEN '18:00:00' AND '19:59:59' THEN '06 PM - 08 PM'\\n            WHEN TIME(`posting_time`) BETWEEN '20:00:00' AND '21:59:59' THEN '08 PM - 10 PM'\\n            WHEN TIME(`posting_time`) BETWEEN '22:00:00' AND '23:59:59' THEN '10 PM - 12 AM'\\n        END AS 'Time Interval',\\n        `name` AS \\\"Bill\\\",\\n        `grand_total` AS 'Sales'\\n    FROM `tabPOS Invoice`\\n    WHERE \\n        DATE(`posting_date`) = DATE(%(date)s)\\n        AND `branch` = %(branch)s\\n        AND `docstatus` = 1\\n        AND `status` IN (\\\"Consolidated\\\", \\\"Paid\\\")\\n) AS sales_by_interval\\nON time_intervals.`Time Interval` = sales_by_interval.`Time Interval`\\nGROUP BY time_intervals.`Time Interval`\\nORDER BY time_intervals.`Order`;\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Time Wise Sales\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/report/today's_sales/__init__.py",
    "content": ""
  },
  {
    "path": "ury/ury/report/today's_sales/today's_sales.json",
    "content": "{\n \"add_total_row\": 0,\n \"columns\": [],\n \"creation\": \"2023-09-29 14:44:32.146055\",\n \"disable_prepared_report\": 0,\n \"disabled\": 0,\n \"docstatus\": 0,\n \"doctype\": \"Report\",\n \"filters\": [\n  {\n   \"fieldname\": \"branch\",\n   \"fieldtype\": \"Link\",\n   \"label\": \"Branch\",\n   \"mandatory\": 1,\n   \"options\": \"Branch\",\n   \"wildcard_filter\": 0\n  }\n ],\n \"idx\": 0,\n \"is_standard\": \"Yes\",\n \"modified\": \"2024-01-07 18:53:11.244041\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"Today's Sales\",\n \"owner\": \"Administrator\",\n \"prepared_report\": 0,\n \"query\": \"SELECT\\n    DAYNAME(curdate()) AS \\\"Today\\\",\\n    COUNT(b.`name`) AS \\\"Total Invoices\\\",\\n    ROUND(SUM(b.`net_total`),2) AS \\\"Item Total\\\",\\n    ROUND(SUM(b.`total_taxes_and_charges`),2) AS \\\"Total Taxes and Charges\\\",\\n    ROUND(SUM(b.`grand_total`),2) AS \\\"Grand Total\\\",\\n    ROUND(SUM(b.`grand_total` - b.`rounded_total`),2) AS \\\"Round Off\\\",\\n    ROUND(SUM(b.`rounded_total` - b.`paid_amount` + b.`change_amount`), 2)AS \\\"Cash Discounts\\\"\\nFROM `tabPOS Invoice` b\\nLEFT JOIN `tabURY Report Settings` rs ON (\\n    rs.`branch` = %(branch)s\\n)\\nWHERE\\n    b.`branch` = %(branch)s\\n    AND b.`docstatus` = 1\\n    AND b.`status` IN (\\\"Consolidated\\\", \\\"Paid\\\") \\n    AND(\\n        ((rs.`hours` IS NULL OR rs.`hours` = 0) AND b.`posting_date` = curdate())\\n        OR (rs.`hours` > 0 AND TIMESTAMP(b.`posting_date`, b.`posting_time`) <= TIMESTAMP(DATE_ADD(curdate(), INTERVAL 1 DAY), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')) AND TIMESTAMP(b.`posting_date`, b.`posting_time`) >= TIMESTAMP(curdate(), CONCAT(LPAD(rs.`hours`, 2, '0'), ':00:00')))\\n        OR (rs.`branch` IS NULL AND b.`posting_date` = curdate())\\n    )\",\n \"ref_doctype\": \"POS Invoice\",\n \"report_name\": \"Today's Sales\",\n \"report_type\": \"Query Report\",\n \"roles\": [\n  {\n   \"role\": \"Accounts Manager\"\n  },\n  {\n   \"role\": \"Accounts User\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  }\n ]\n}"
  },
  {
    "path": "ury/ury/workspace/ury/ury.json",
    "content": "{\n \"charts\": [],\n \"content\": \"[{\\\"id\\\":\\\"3XhkKI6EJA\\\",\\\"type\\\":\\\"card\\\",\\\"data\\\":{\\\"card_name\\\":\\\"URY Setup\\\",\\\"col\\\":3}},{\\\"id\\\":\\\"YXLBzy46Bq\\\",\\\"type\\\":\\\"custom_block\\\",\\\"data\\\":{\\\"custom_block_name\\\":\\\"URY POS\\\",\\\"col\\\":2}},{\\\"id\\\":\\\"OJsNZQxJi3\\\",\\\"type\\\":\\\"custom_block\\\",\\\"data\\\":{\\\"custom_block_name\\\":\\\"POS V1\\\",\\\"col\\\":2}}]\",\n \"creation\": \"2023-11-30 12:04:38.330350\",\n \"custom_blocks\": [\n  {\n   \"custom_block_name\": \"URY POS\",\n   \"label\": \"URY POS\"\n  },\n  {\n   \"custom_block_name\": \"POS V1\",\n   \"label\": \"POS V1\"\n  }\n ],\n \"docstatus\": 0,\n \"doctype\": \"Workspace\",\n \"for_user\": \"\",\n \"hide_custom\": 0,\n \"icon\": \"milestone\",\n \"idx\": 0,\n \"is_hidden\": 0,\n \"label\": \"URY\",\n \"links\": [\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"URY Setup\",\n   \"link_count\": 8,\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Card Break\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"Branch\",\n   \"link_count\": 0,\n   \"link_to\": \"Branch\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"URY Restaurant\",\n   \"link_count\": 0,\n   \"link_to\": \"URY Restaurant\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"POS Profile\",\n   \"link_count\": 0,\n   \"link_to\": \"POS Profile\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"URY Menu\",\n   \"link_count\": 0,\n   \"link_to\": \"URY Menu\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"URY Menu Course\",\n   \"link_count\": 0,\n   \"link_to\": \"URY Menu Course\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"URY Table\",\n   \"link_count\": 0,\n   \"link_to\": \"URY Table\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"URY Room\",\n   \"link_count\": 0,\n   \"link_to\": \"URY Room\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  },\n  {\n   \"hidden\": 0,\n   \"is_query_report\": 0,\n   \"label\": \"Network Printer Settings\",\n   \"link_count\": 0,\n   \"link_to\": \"Network Printer Settings\",\n   \"link_type\": \"DocType\",\n   \"onboard\": 0,\n   \"type\": \"Link\"\n  }\n ],\n \"modified\": \"2026-01-16 11:47:19.008936\",\n \"modified_by\": \"Administrator\",\n \"module\": \"URY\",\n \"name\": \"URY\",\n \"number_cards\": [],\n \"owner\": \"Administrator\",\n \"parent_page\": \"\",\n \"public\": 1,\n \"quick_lists\": [],\n \"roles\": [\n  {\n   \"role\": \"URY Captain\"\n  },\n  {\n   \"role\": \"URY Manager\"\n  },\n  {\n   \"role\": \"URY Cashier\"\n  }\n ],\n \"sequence_id\": 31.0,\n \"shortcuts\": [],\n \"title\": \"URY\"\n}"
  },
  {
    "path": "ury/ury_pos/api.py",
    "content": "import frappe\nfrom frappe import _\nfrom datetime import date, datetime, timedelta\nfrom frappe.utils import validate_phone_number\n\n\n#GetTable  decripted temporarily\n# @frappe.whitelist()\n# def getTable(room):\n#     branch_name = getBranch()   \n#     tables = frappe.get_all(\n#         \"URY Table\",\n#         fields=[\"name\", \"occupied\", \"latest_invoice_time\", \"is_take_away\", \"restaurant_room\",\"table_shape\",\"no_of_seats\",\"layout_x\",\"layout_y\"],\n#         filters={\"branch\": branch_name,\"restaurant_room\":room,}\n#     )    \n#     return tables\n\n@frappe.whitelist()\ndef getRestaurantMenu(pos_profile, room=None, order_type=None):\n    menu_items = []\n    menu_items_with_image = []\n\n    user_role = frappe.get_roles()\n\n    pos_profile = frappe.get_doc(\"POS Profile\", pos_profile)\n\n    cashier = any(\n        role.role in user_role for role in pos_profile.role_allowed_for_billing\n    )\n    branch_name = getBranch()\n    restaurant = frappe.db.get_value(\"URY Restaurant\", {\"branch\": branch_name}, \"name\")\n    \n    if room:\n    \n        room_wise_menu = frappe.db.get_value(\n            \"URY Restaurant\", restaurant, \"room_wise_menu\"\n        )\n        \n        if room_wise_menu:\n            menu = frappe.db.get_value(\n                \"Menu for Room\",\n                {\"parent\": restaurant, \"room\": room},\n                \"menu\"\n            )\n            if not menu:\n                 menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n        else:\n            menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n\n    elif cashier and order_type:\n        order_type_wise_menu = frappe.db.get_value(\n            \"URY Restaurant\", restaurant, \"order_type_wise_menu\"\n        )\n    \n        if order_type_wise_menu:\n            menu = frappe.db.get_value(\n                \"Order Type Menu\",\n                {\"parent\": restaurant, \"order_type\": order_type},\n                \"menu\"\n            )\n            if not menu:\n                 menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n    \n        else:\n            menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n\n    # Default menu if nothing is selected\n    else:\n        menu = frappe.db.get_value(\"URY Restaurant\", restaurant, \"active_menu\")\n    \n    if not menu:\n        frappe.throw(_(\"Please set an active menu for Restaurant {0}\").format(restaurant))\n    \n    \n    # Get menu items (your existing code)\n    menu_items = frappe.get_all(\n        \"URY Menu Item\",\n        filters={\"parent\": menu, \"disabled\": 0},\n        fields=[\"item\", \"item_name\", \"rate\", \"special_dish\", \"disabled\", \"course\"],\n        order_by=\"item_name asc\"\n    )\n    \n    menu_items_with_image = [\n        {\n            \"item\": item.item,\n            \"item_name\": _(item.item_name) if item.item_name else item.item_name,\n            \"rate\": item.rate,\n            \"special_dish\": item.special_dish,\n            \"disabled\": item.disabled,\n            \"item_image\": frappe.db.get_value(\"Item\", item.item, \"image\"),\n            \"course\": item.course,\n            \"course_label\": _(item.course) if item.course else item.course,\n        }\n        for item in menu_items\n    ]\n    modified = frappe.db.get_value(\"URY Menu\", menu, \"modified\")\n    \n    \n    return {\n        \"items\": menu_items_with_image,\n        \"modified_time\": modified,\n        \"name\": menu\n    }\n\n@frappe.whitelist()\ndef getMenuCourses():\n    courses = frappe.get_all(\"URY Menu Course\", fields=[\"name\"])\n    return [{\"name\": d.name, \"label\": _(d.name)} for d in courses]\n\n@frappe.whitelist()\ndef getBranch():\n    user = frappe.session.user\n    if user != \"Administrator\":\n        sql_query = \"\"\"\n            SELECT b.branch\n            FROM `tabURY User` AS a\n            INNER JOIN `tabBranch` AS b ON a.parent = b.name\n            WHERE a.user = %s\n        \"\"\"\n        branch_array = frappe.db.sql(sql_query, user, as_dict=True)\n        if not branch_array:\n            frappe.throw(\"User is not Associated with any Branch.Please refresh Page\")\n\n        branch_name = branch_array[0].get(\"branch\")\n\n        return branch_name\n\n@frappe.whitelist()\ndef getBranchRoom():\n    user = frappe.session.user\n    if user != \"Administrator\":\n        sql_query = \"\"\"\n            SELECT b.branch , a.room\n            FROM `tabURY User` AS a\n            INNER JOIN `tabBranch` AS b ON a.parent = b.name\n            WHERE a.user = %s\n        \"\"\"\n        branch_array = frappe.db.sql(sql_query, user, as_dict=True)\n        \n        branch_name = branch_array[0].get(\"branch\")\n        room_name = branch_array[0].get(\"room\")\n    \n        if not branch_name:\n            frappe.throw(\"Branch information is missing for the user. Please contact your administrator.\")\n\n        if not room_name:\n            frappe.throw(\"No room assigned to this user. Please contact your administrator.\")\n\n        return [{\n            \"name\":room_name ,\n            \"branch\": branch_name,\n        }]\n\n@frappe.whitelist()\ndef getRoom():\n    user = frappe.session.user\n    if user != \"Administrator\":\n        sql_query = \"\"\"\n            SELECT b.branch, a.room\n            FROM `tabURY User` AS a\n            INNER JOIN `tabBranch` AS b ON a.parent = b.name\n            WHERE a.user = %s\n        \"\"\"\n        branch_array = frappe.db.sql(sql_query, user, as_dict=True)\n        \n        if not branch_array:\n            frappe.throw(\"No branch or room information found for the user. Please contact your administrator.\")\n        \n        room_details = [\n            {\n                \"name\": row.get(\"room\"),\n                \"branch\": row.get(\"branch\")\n            } \n            for row in branch_array\n        ]\n\n        return room_details\n\n@frappe.whitelist()\ndef getModeOfPayment():\n    posDetails = getPosProfile()\n    posProfile = posDetails[\"pos_profile\"]\n    posProfiles = frappe.get_doc(\"POS Profile\", posProfile)\n    mode_of_payments = posProfiles.payments\n    modeOfPayments = []\n    for mop in mode_of_payments:\n        modeOfPayments.append(\n            {\"mode_of_payment\": mop.mode_of_payment, \"opening_amount\": float(0)}\n        )\n    return modeOfPayments\n\n@frappe.whitelist()\ndef getInvoiceForCashier(status, cashier, limit, limit_start):\n    branch = getBranch()\n    updatedlist = []\n    limit = int(limit)+1\n    limit_start = int(limit_start)\n    if status == \"Draft\":\n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number, \n                posting_date, rounded_total, order_type \n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s AND cashier = %s\n            AND (invoice_printed = 1 OR (invoice_printed = 0 AND COALESCE(restaurant_table, '') = ''))\n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, status, cashier, limit,limit_start),\n            as_dict=True,\n        )\n        updatedlist.extend(invoices)\n    elif status == \"Unbilled\":\n        \n        docstatus = \"Draft\"\n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number, \n                posting_date, rounded_total, order_type \n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s AND cashier = %s\n            AND (invoice_printed = 0 AND restaurant_table IS NOT NULL)\n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, docstatus, cashier, limit, limit_start),\n            as_dict=True,\n        )\n        updatedlist.extend(invoices)\n    elif status == \"Recently Paid\":\n        docstatus = \"Paid\"\n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number,\n                posting_date, rounded_total, order_type,additional_discount_percentage,discount_amount \n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s AND cashier = %s\n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, docstatus, cashier, limit, limit_start),\n            as_dict=True,\n        )\n        updatedlist.extend(invoices)    \n    else:\n        \n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number,\n                posting_date, rounded_total, order_type,additional_discount_percentage,discount_amount\n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s AND cashier = %s\n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, status, cashier, limit, limit_start),\n            as_dict=True,\n        )\n\n        updatedlist.extend(invoices)\n    if len(updatedlist) == limit and status != \"Recently Paid\":\n            next = True\n            updatedlist.pop()\n    else:\n            next = False   \n    return  { \"data\":updatedlist,\"next\":next}\n\n\n\n@frappe.whitelist()\ndef getPosInvoice(status, limit, limit_start):\n    branch = getBranch()\n    updatedlist = []\n    limit = int(limit)+1\n    limit_start = int(limit_start)\n    if status == \"Draft\":\n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number, \n                posting_date, rounded_total, order_type \n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s \n            AND (invoice_printed = 1 OR (invoice_printed = 0 AND COALESCE(restaurant_table, '') = ''))\n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, status, limit,limit_start),\n            as_dict=True,\n        )\n        updatedlist.extend(invoices)\n    elif status == \"Unbilled\":\n        \n        docstatus = \"Draft\"\n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number, \n                posting_date, rounded_total, order_type \n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s \n            AND (invoice_printed = 0 AND restaurant_table IS NOT NULL)\n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, docstatus, limit, limit_start),\n            as_dict=True,\n        )\n        updatedlist.extend(invoices)\n    elif status == \"Recently Paid\":\n        docstatus = \"Paid\"\n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number,\n                posting_date, rounded_total, order_type,additional_discount_percentage,discount_amount \n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s \n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, docstatus, limit, limit_start),\n            as_dict=True,\n        )\n        updatedlist.extend(invoices)    \n    else:\n        \n        invoices = frappe.db.sql(\n            \"\"\"\n            SELECT \n                name, invoice_printed, grand_total, restaurant_table, \n                cashier, waiter, net_total, posting_time, \n                total_taxes_and_charges, customer, status, mobile_number,\n                posting_date, rounded_total, order_type,additional_discount_percentage,discount_amount\n            FROM `tabPOS Invoice` \n            WHERE branch = %s AND status = %s \n            ORDER BY modified desc\n            LIMIT %s OFFSET %s\n            \"\"\",\n            (branch, status, limit, limit_start),\n            as_dict=True,\n        )\n\n        updatedlist.extend(invoices)\n    if len(updatedlist) == limit and status != \"Recently Paid\":\n            next = True\n            updatedlist.pop()\n    else:\n            next = False   \n    return  { \"data\":updatedlist,\"next\":next}\n\n\n@frappe.whitelist()\ndef searchPosInvoice(query,status):\n    if not query:\n        return {\"data\": [], \"next\": False}\n    query = query.lower()\n    filters = {\"status\": \"Paid\" if status == \"Recently Paid\" else status}\n    \n    # Add additional conditions for Unbilled status\n    if status == \"Unbilled\":\n        filters.update({\n            \"status\":\"draft\",\n            \"restaurant_table\": [\"not in\", [None, \"\"]],  # Check if restaurant_table has value\n            \"invoice_printed\": 0  # Check if invoice_printed is 0\n        })\n    pos_invoices = frappe.get_all(\n        \"POS Invoice\",\n        filters=filters,           \n        or_filters=[\n            [\"name\", \"like\", f\"%{query}%\"],\n            [\"customer\", \"like\", f\"%{query}%\"],\n            [\"mobile_number\", \"like\", f\"%{query}%\"],\n        ],\n        fields=[\"name\", \"customer\", \"grand_total\", \"posting_date\", \"posting_time\", \"order_type\", \"restaurant_table\",\"status\",\"grand_total\",\"rounded_total\",\"net_total\",\"mobile_number\"],\n        limit_page_length=10 \n    )\n    \n    return {\"data\": pos_invoices, \"next\": len(pos_invoices) == 10}\n    \n\n@frappe.whitelist()\ndef get_select_field_options():\n    options = frappe.get_meta(\"POS Invoice\").get_field(\"order_type\").options\n    if options:\n        return [{\"name\": option} for option in options.split(\"\\n\")]\n    else:\n        return []\n\n\n@frappe.whitelist()\ndef fav_items(customer):\n    pos_invoices = frappe.get_all(\n        \"POS Invoice\", filters={\"customer\": customer}, fields=[\"name\"]\n    )\n    item_qty = {}\n\n    for invoice in pos_invoices:\n        pos_invoice = frappe.get_doc(\"POS Invoice\", invoice.name)\n        for item in pos_invoice.items:\n            item_name = item.item_name\n            qty = item.qty\n            if item_name not in item_qty:\n                item_qty[item_name] = 0\n            item_qty[item_name] += qty\n\n    favorite_items = [\n        {\"item_name\": item_name, \"qty\": qty} for item_name, qty in item_qty.items()\n    ]\n    return favorite_items\n\n@frappe.whitelist()\ndef getCashier(room):\n    branch = getBranch()\n    cashier = None\n    pos_opening_list = frappe.db.sql(\"\"\"\n        SELECT DISTINCT `tabPOS Opening Entry`.name \n        FROM `tabPOS Opening Entry`\n        INNER JOIN `tabMultiple Rooms` \n        ON `tabMultiple Rooms`.parent = `tabPOS Opening Entry`.name\n        WHERE `tabPOS Opening Entry`.branch = %s\n        AND `tabPOS Opening Entry`.status = 'Open'\n        AND `tabPOS Opening Entry`.docstatus = 1\n        AND `tabMultiple Rooms`.room = %s\n    \"\"\", (branch, room), as_dict=True)\n    if pos_opening_list:\n        cashier = frappe.db.get_value(\n            \"POS Opening Entry\",\n            {\"name\": pos_opening_list[0].name},\n            \"user\",)\n    return cashier       \n    \n\n@frappe.whitelist()\ndef getPosProfile():\n    branchName = getBranch()\n    waiter = frappe.session.user\n    bill_present = False\n    qz_host = None\n    printer = None\n    cashier = None\n    owner = None\n    posProfile = frappe.db.exists(\"POS Profile\", {\"branch\": branchName})\n    pos_profiles = frappe.get_doc(\"POS Profile\", posProfile)\n    global_defaults = frappe.get_single('Global Defaults')\n    disable_rounded_total = global_defaults.disable_rounded_total\n    \n\n    if pos_profiles.branch == branchName:\n        pos_profile_name = pos_profiles.name\n        warehouse = pos_profiles.warehouse\n        branch = pos_profiles.branch\n        company = pos_profiles.company\n        tableAttention = pos_profiles.table_attention_time\n        get_cashier = frappe.get_doc(\"POS Profile\", pos_profile_name)\n        print_format = pos_profiles.print_format\n        paid_limit=pos_profiles.paid_limit\n        enable_discount = pos_profiles.custom_enable_discount\n        multiple_cashier = pos_profiles.custom_enable_multiple_cashier\n        edit_order_type = pos_profiles.custom_edit_order_type\n        enable_kot_reprint = pos_profiles.custom_enable_kot_reprint\n        if multiple_cashier:\n            details = getBranchRoom()\n            room = details[0].get('name') \n            branch = details[0].get('branch')\n\n            pos_opening_list = frappe.db.sql(\"\"\"\n                SELECT DISTINCT `tabPOS Opening Entry`.name \n                FROM `tabPOS Opening Entry`\n                INNER JOIN `tabMultiple Rooms` \n                ON `tabMultiple Rooms`.parent = `tabPOS Opening Entry`.name\n                WHERE `tabPOS Opening Entry`.branch = %s\n                AND `tabPOS Opening Entry`.status = 'Open'\n                AND `tabPOS Opening Entry`.docstatus = 1\n                AND `tabMultiple Rooms`.room = %s\n            \"\"\", (branch, room), as_dict=True)\n            if pos_opening_list:\n                pos_opened_cashier = frappe.db.get_value(\n                    \"POS Opening Entry\",\n                    {\"name\": pos_opening_list[0].name},\n                    \"user\",)\n            else:\n                pos_opened_cashier = None\n            for user_details in get_cashier.applicable_for_users:\n                if user_details.custom_main_cashier:\n                    owner = user_details.user\n                \n                if frappe.session.user == owner:\n                    cashier = owner\n                else:\n                    cashier = pos_opened_cashier    \n                \n        else:    \n            cashier = get_cashier.applicable_for_users[0].user\n            owner = get_cashier.applicable_for_users[0].user\n        \n        qz_print = pos_profiles.qz_print\n        print_type = None\n\n        for pos_profile in pos_profiles.printer_settings:\n            if pos_profile.bill == 1:\n                printer = pos_profile.printer\n                bill_present = True\n                break\n\n        if qz_print == 1:\n            print_type = \"qz\"\n            qz_host = pos_profiles.qz_host\n\n        elif bill_present == True:\n            print_type = \"network\"\n\n        else:\n            print_type = \"socket\"\n\n    invoice_details = {\n        \"pos_profile\": pos_profile_name,\n        \"branch\": branch,\n        \"company\": company,\n        \"waiter\": waiter,\n        \"warehouse\": warehouse,\n        \"cashier\": cashier,\n        \"print_format\": print_format,\n        \"qz_print\": qz_print,\n        \"qz_host\": qz_host,\n        \"printer\": printer,\n        \"print_type\": print_type,\n        \"tableAttention\": tableAttention,\n        \"paid_limit\":paid_limit,\n        \"disable_rounded_total\":disable_rounded_total,\n        \"enable_discount\":enable_discount,\n        \"multiple_cashier\":multiple_cashier,\n        \"owner\":owner,\n        \"edit_order_type\":edit_order_type,\n        \"enable_kot_reprint\":enable_kot_reprint\n\n    }\n\n    return invoice_details\n\n\n@frappe.whitelist()\ndef getPosInvoiceItems(invoice):\n    itemDetails = []\n    taxDetails = []\n    orderdItems = frappe.get_doc(\"POS Invoice\", invoice)\n    posItems = orderdItems.items\n    for items in posItems:\n        item_name = items.item_name\n        qty = items.qty\n        amount = items.rate\n        itemDetails.append(\n            {\n                \"item_name\": item_name,\n                \"qty\": qty,\n                \"amount\": amount,\n            }\n        )\n    taxDetail = orderdItems.taxes\n    for tax in taxDetail:\n        description = tax.description\n        rate = tax.tax_amount\n        taxDetails.append(\n            {\n                \"description\": description,\n                \"rate\": rate,\n            }\n        )\n    return itemDetails, taxDetails\n\n\n@frappe.whitelist()\ndef posOpening():\n    branchName = getBranch()\n    pos_opening_list = frappe.get_all(\n        \"POS Opening Entry\",\n        fields=[\"name\", \"docstatus\", \"status\", \"posting_date\"],\n        filters={\"branch\": branchName},\n    )\n    flag = 1\n    for pos_opening in pos_opening_list:\n        if pos_opening.status == \"Open\" and pos_opening.docstatus == 1:\n            flag = 0\n    if flag == 1:\n        frappe.msgprint(title=\"Message\", indicator=\"red\", msg=(\"Please Open POS Entry\"))\n    return flag\n\n\n@frappe.whitelist()\ndef getAggregator():\n    branchName = getBranch()\n    aggregatorList = frappe.get_all(\n        \"Aggregator Settings\",\n        fields=[\"customer\"],\n        filters={\"parent\": branchName, \"parenttype\": \"Branch\"},\n    )\n    return aggregatorList\n\n\n@frappe.whitelist()\ndef getAggregatorItem(aggregator):\n    branchName = getBranch()\n    aggregatorItem = []\n    aggregatorItemList = []\n    priceList = frappe.db.get_value(\n        \"Aggregator Settings\",\n        {\"customer\": aggregator, \"parent\": branchName, \"parenttype\": \"Branch\"},\n        \"price_list\",\n    )\n    aggregatorItem = frappe.get_all(\n        \"Item Price\",\n        fields=[\"item_code\", \"item_name\", \"price_list_rate\"],\n        filters={\"selling\": 1, \"price_list\": priceList},\n    )\n    aggregatorItemList = [\n        {\n            \"item\": item.item_code,\n            \"item_name\": item.item_name,\n            \"rate\": item.price_list_rate,\n            \"item_image\": frappe.db.get_value(\"Item\", item.item, \"image\"),\n        }\n        for item in aggregatorItem\n        if not frappe.db.get_value(\"Item\", item.item_code, \"disabled\")\n    ]\n    return aggregatorItemList\n\n@frappe.whitelist()\ndef getAggregatorMOP(aggregator):\n    branchName = getBranch()\n    \n    modeOfPayment = frappe.db.get_value(\n        \"Aggregator Settings\",\n        {\"customer\": aggregator, \"parent\": branchName, \"parenttype\": \"Branch\"},\n        \"mode_of_payments\",\n    )\n    modeOfPaymentsList = []\n    modeOfPaymentsList.append(\n            {\"mode_of_payment\": modeOfPayment, \"opening_amount\": float(0)}\n    )\n    return modeOfPaymentsList\n@frappe.whitelist()\ndef create_customer(customer_name, mobile_number=None, customer_group=\"Individual\", territory=\"India\"):\n    if not customer_name:\n        frappe.throw(\"Customer name is required\")\n    if not mobile_number:\n        frappe.throw(\"Mobile Number is required\")\n    try:\n        validate_phone_number(mobile_number, throw=True)\n    except Exception:\n        frappe.throw(\"Invalid mobile number format\")\n\n    \"\"\"Create a new customer\"\"\"\n    try:\n        customer = frappe.get_doc({\n            \"doctype\": \"Customer\",\n            \"customer_name\": customer_name,\n            \"mobile_number\": mobile_number,\n            \"customer_group\": customer_group,\n            \"territory\": territory\n        })\n        customer.insert(ignore_permissions=True)\n        frappe.db.commit()\n\n        return {\n            \"status\": \"success\",\n            \"message\": \"Customer created successfully\",\n            \"customer_name\": customer_name,\n            \"mobile_number\": mobile_number,\n            \"customer_group\": customer_group,\n            \"territory\": territory\n        }\n\n    except Exception as e:\n        frappe.log_error(message=frappe.get_traceback(), title=\"Customer Creation Failed\")\n        return {\n            \"status\": \"error\",\n            \"message\": str(e)\n        }\n\n@frappe.whitelist()\ndef validate_pos_close(pos_profile): \n    enable_unclosed_pos_check = frappe.db.get_value(\"POS Profile\",pos_profile,\"custom_daily_pos_close\")\n    \n    if enable_unclosed_pos_check:\n        current_datetime = frappe.utils.now_datetime()\n        start_of_day = current_datetime.replace(hour=5, minute=0, second=0, microsecond=0)\n        \n        if current_datetime > start_of_day:\n            previous_day = start_of_day - timedelta(days=1)\n            \n        else:\n            previous_day = start_of_day\n    \n        unclosed_pos_opening = frappe.db.exists(\n            \"POS Opening Entry\",\n            {\n                \"posting_date\": previous_day.date(),\n                \"status\": \"Open\",\n                \"pos_profile\": pos_profile,\n                \"docstatus\": 1\n            }\n        )\n    \n        if unclosed_pos_opening:\n            return \"Failed\"\n        \n        return \"Success\"\n    \n    return \"Success\"\n\n"
  },
  {
    "path": "ury/www/__init__.py",
    "content": ""
  },
  {
    "path": "ury/www/pos.py",
    "content": "import json\nimport re\n\nimport frappe\nimport frappe.sessions\nfrom frappe import _\nfrom frappe.utils.telemetry import capture\n\nno_cache = 1\n\nSCRIPT_TAG_PATTERN = re.compile(r\"\\<script[^<]*\\</script\\>\")\nCLOSING_SCRIPT_TAG_PATTERN = re.compile(r\"</script\\>\")\n\n\ndef get_context(context):\n\tcsrf_token = frappe.sessions.get_csrf_token()\n\t# Manually commit the CSRF token here\n\tfrappe.db.commit()  # nosemgrep\n\n\tif frappe.session.user == \"Guest\":\n\t\tboot = frappe.website.utils.get_boot_data()\n\telse:\n\t\ttry:\n\t\t\tboot = frappe.sessions.get()\n\t\texcept Exception as e:\n\t\t\traise frappe.SessionBootFailed from e\n\n\t# add server_script_enabled in boot\n\tif \"server_script_enabled\" in frappe.conf:\n\t\tenabled = frappe.conf.server_script_enabled\n\telse:\n\t\tenabled = True\n\tboot[\"server_script_enabled\"] = enabled\n\n\tboot_json = frappe.as_json(boot, indent=None, separators=(\",\", \":\"))\n\tboot_json = SCRIPT_TAG_PATTERN.sub(\"\", boot_json)\n\n\tboot_json = CLOSING_SCRIPT_TAG_PATTERN.sub(\"\", boot_json)\n\tboot_json = json.dumps(boot_json)\n\n\tcontext.update(\n\t\t{\"build_version\": frappe.utils.get_build_version(), \"boot\": boot_json, \"csrf_token\": csrf_token}\n\t)\n\treturn context\n\n\n@frappe.whitelist(methods=[\"POST\"], allow_guest=True)\ndef get_context_for_dev():\n\tif not frappe.conf.developer_mode:\n\t\tfrappe.throw(_(\"This method is only meant for developer mode\"))\n\treturn json.loads(get_boot())\n\n\ndef get_boot():\n\ttry:\n\t\tboot = frappe.sessions.get()\n\texcept Exception as e:\n\t\traise frappe.SessionBootFailed from e\n\n\tboot[\"push_relay_server_url\"] = frappe.conf.get(\"push_relay_server_url\")\n\tboot_json = frappe.as_json(boot, indent=None, separators=(\",\", \":\"))\n\tboot_json = SCRIPT_TAG_PATTERN.sub(\"\", boot_json)\n\n\tboot_json = CLOSING_SCRIPT_TAG_PATTERN.sub(\"\", boot_json)\n\tboot_json = json.dumps(boot_json)\n\n\treturn boot_json"
  },
  {
    "path": "urypos/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "urypos/.vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"Vue.volar\", \"Vue.vscode-typescript-vue-plugin\"]\n}\n"
  },
  {
    "path": "urypos/README.md",
    "content": "# Vue 3 + Vite\n\nThis template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.\n\n## Recommended IDE Setup\n\n- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).\n"
  },
  {
    "path": "urypos/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/ury.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>URY POS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script>window.csrf_token = '{{ frappe.session.csrf_token }}';</script>\n    <script type=\"module\" src=\"/src/main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "urypos/package.json",
    "content": "{\n  \"name\": \"urypos\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build --base=/assets/ury/urypos/ && yarn copy-html-entry\",\n    \"preview\": \"vite preview\",\n    \"copy-html-entry\": \"cp ../ury/public/urypos/index.html ../ury/www/urypos.html\"\n  },\n  \"dependencies\": {\n    \"socket.io-client\": \"^4.5.1\",\n    \"vue\": \"^3.3.4\",\n    \"vue-router\": \"^4\",\n    \"@syncfusion/ej2-vue-calendars\": \"^22.2.10\",\n    \"axios\": \"^1.3.4\",\n    \"flowbite\": \"^1.6.5\",\n    \"flowbite-vue\": \"0.0.10\",\n    \"frappe-js-sdk\": \"^1.3.5\",\n    \"jsrsasign\": \"^10.8.6\",\n    \"moment\": \"^2.29.4\",\n    \"pinia\": \"^2.0.35\",\n    \"pnpm\": \"^7.33.6\",\n    \"qz-tray\": \"^2.2.2\",\n    \"vue-datepicker\": \"^1.3.0\",\n    \"vue-datepicker-next\": \"^1.0.3\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^4.2.3\",\n    \"autoprefixer\": \"^10.4.15\",\n    \"eslint\": \"^8.34.0\",\n    \"eslint-config-prettier\": \"^8.10.0\",\n    \"eslint-plugin-vue\": \"^9.9.0\",\n    \"postcss\": \"^8.4.29\",\n    \"prettier\": \"2.8.4\",\n    \"prettier-plugin-tailwindcss\": \"^0.2.3\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"vite\": \"^4.5.2\"\n  }\n}\n"
  },
  {
    "path": "urypos/postcss.config.js",
    "content": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "urypos/privateKey.js",
    "content": "export const privateKey = \"PASTE YOUR KEY HERE\";\n"
  },
  {
    "path": "urypos/proxyOptions.js",
    "content": "const common_site_config = require('../../../sites/common_site_config.json');\nconst { webserver_port } = common_site_config;\n\nexport default {\n\t'^/(app|api|assets|files)': {\n\t\ttarget: `http://localhost:${webserver_port}`,\n\t\tws: true,\n\t\trouter: function(req) {\n\t\t\tconst site_name = req.headers.host.split(':')[0];\n\t\t\treturn `http://${site_name}:${webserver_port}`;\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "urypos/src/App.vue",
    "content": "<template>\n  <Header />\n\n  <div class=\"container mx-auto mb-16 p-4\">\n    <NotificationModal />\n\n    <router-view></router-view>\n  </div>\n\n  <Tabs />\n</template>\n\n<script>\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport Tabs from \"./components/bottomTabs.vue\";\nimport Header from \"./components/Header.vue\";\nimport NotificationModal from \"./components/NotificationModal.vue\";\n\nexport default {\n  name: \"App\",\n  components: {\n    Tabs,\n    Header,\n    NotificationModal,\n  },\n  setup() {\n    const auth = useAuthStore();\n    return { auth };\n  },\n  mounted() {\n    this.auth.fetchUserDetails();\n  },\n  computed: {\n    isLoginPage() {\n      return this.$route.path === \"/\";\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/Cart.vue",
    "content": "<template>\n  <div\n    class=\"fixed inset-0 z-50 flex items-center justify-center bg-gray-300 bg-opacity-50 text-lg\"\n    v-if=\"this.invoiceData.invoiceUpdating\"\n  >\n    Updating Order...\n  </div>\n  <div\n    class=\"fixed inset-0 z-50 flex items-center justify-center bg-gray-300 bg-opacity-50 text-lg\"\n    v-if=\"this.invoiceData.kotPrinting\"\n  >\n    KOT Reprinting...\n  </div>\n  <div class=\"mt-5 flex\">\n    <div class=\"flex-grow\">\n      <orderInfo />\n    </div>\n    <div class=\"float-right rounded\" v-if=\"this.menu.cart.length > 0\">\n      <button\n        class=\"mr-4 rounded px-4 py-2 shadow\"\n        v-if=\"this.invoiceData.showUpdateButtton === true\"\n        @click=\"this.invoiceData.invoiceCreation()\"\n      >\n        Update\n      </button>\n      <button\n      class=\"mr-4 rounded py-2 px-4 shadow\"\n      v-if=\"this.invoiceData.enableKotReprint\"\n      @click=\"this.invoiceData.kotReprint()\"\n      >\n        KOT Reprint\n      </button>\n      <button\n        class=\"rounded px-4 py-2 shadow\"\n        v-if=\"\n          (this.recentOrders.invoicePrinted === 0 ||\n            this.table.invoicePrinted === 0) &&\n          !this.auth.cashier\n        \"\n        @click=\"this.invoiceData.showCancelInvoiceModal()\"\n      >\n        Cancel\n      </button>\n    </div>\n  </div>\n  <div\n    class=\"flex h-screen items-center justify-center\"\n    v-if=\"this.menu.cart.length === 0\"\n  >\n    <div class=\"text-center\">Nothing to show here</div>\n  </div>\n\n  <div class=\"mt-5 border shadow\" v-if=\"this.menu.cart.length > 0\">\n    <div\n      class=\"cart-item-details grid w-full grid-cols-3 gap-4 md:w-full lg:w-full\"\n    >\n      <h3\n        class=\"ml-3 mt-2 text-base font-semibold text-gray-900 dark:text-white\"\n      >\n        Item Name\n      </h3>\n      <h3\n        class=\"ml-3 mt-2 text-center text-lg font-semibold text-gray-900 dark:text-white\"\n      >\n        Quantity\n      </h3>\n    </div>\n    <div\n      class=\"cart-item-details ml-3 mt-2 grid w-full grid-cols-3 gap-4 pb-2 md:w-full lg:w-full\"\n      v-for=\"(cart_item, index) in this.menu.cart\"\n      :key=\"index\"\n    >\n      <h3 class=\"w-full text-base text-gray-900 dark:text-white\">\n        {{ cart_item.item_name }}\n      </h3>\n      <input\n        type=\"number\"\n        id=\"qty_input_cart\"\n        name=\"qty_input_cart\"\n        class=\"block w-full border-none text-center text-base text-gray-900 dark:text-white\"\n        :value=\"parseInt(cart_item.qty)\"\n        @input=\"cart_item.qty = $event.target.value\"\n        @click=\"\n          this.menu.showModal(cart_item);\n          menu.showDialogCart = true;\n        \"\n        readonly\n      />\n      <div class=\"items-center text-center\">\n        <button\n          class=\"p-2 text-center\"\n          type=\"button\"\n          :disabled=\"this.recentOrders.restaurantTable\"            \n          @click=\"\n            (this.recentOrders.editPrintedInvoice === 0 ||\n              this.auth.removeTableOrderItem === 1) &&\n              this.menu.removeItemFromCart(index)\n          \"\n        >\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            width=\"25\"\n            height=\"25\"\n            :style=\"{ fill: this.menu.setColorForBilledInvoice }\"\n            class=\"bi bi-trash\"\n            viewBox=\"0 0 16 16\"\n          >\n            <path\n              d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z\"\n            ></path>\n            <path\n              d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z\"\n            ></path>\n          </svg>\n        </button>\n      </div>\n    </div>\n  </div>\n  <div class=\"relative mt-8\" v-if=\"this.menu.cart.length > 0\">\n    <label\n      for=\"grand_total\"\n      class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n    >\n      Grand Total\n    </label>\n    <input\n      class=\"grand_total mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n      :value=\"\n        this.menu.grand_total || this.table.grandTotal || invoiceData.grandTotal\n      \"\n      readonly\n    />\n    <label\n      for=\"aggregatorId\"\n      class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n      v-if=\"this.menu.selectedOrderType === 'Aggregators'\"\n    >\n      Aggregator ID\n    </label>\n    <input\n      v-if=\"this.menu.selectedOrderType === 'Aggregators'\"\n      id=\"aggregatorId\"\n      class=\"mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n      v-model=\"this.menu.aggregatorId\"\n    />\n    <label\n      for=\"Comments\"\n      class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n    >\n      Comments\n    </label>\n    <input\n      id=\"comments\"\n      class=\"mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n      v-model=\"this.menu.comments\"\n    />\n  </div>\n\n  <div\n    v-if=\"this.invoiceData.cancelInvoiceFlag === true\"\n    class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n  >\n    <div class=\"mt-20 flex items-center justify-center\">\n      <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n        <div class=\"flex justify-end\">\n          <span class=\"sr-only\">Close</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"h-5 w-5\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            @click=\"this.invoiceData.cancelInvoiceFlag = false\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M6 18L18 6M6 6l12 12\"\n            />\n          </svg>\n        </div>\n        <h2\n          class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n        >\n          Are you sure to cancel\n        </h2>\n        <div class=\"relative\">\n          <label\n            for=\"cancelReason\"\n            class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n          >\n            Reason\n          </label>\n          <input\n            type=\"text\"\n            id=\"cancelReason\"\n            class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n            v-model=\"this.invoiceData.cancelReason\"\n          />\n        </div>\n        <div class=\"flex justify-end\">\n          <button\n            @click=\"this.invoiceData.cancelInvoiceFlag = false\"\n            class=\"mr-3 mt-6 rounded border border-gray-300 bg-gray-50 px-3 py-2\"\n          >\n            No\n          </button>\n          <button\n            @click=\"handleConfirmCancellation()\"\n            class=\"mt-6 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n          >\n            Yes\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div\n    v-if=\"menu.showDialogCart\"\n    class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n  >\n    <div class=\"mt-10 flex items-center justify-center\">\n      <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n        <div class=\"flex justify-end\">\n          <span class=\"sr-only\">Close</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"h-5 w-5\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            @click=\"menu.showDialogCart = false\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M6 18L18 6M6 6l12 12\"\n            />\n          </svg>\n        </div>\n\n        <h2\n          class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n        >\n          Enter Details\n        </h2>\n        <div class=\"relative\">\n          <label\n            for=\"quantity\"\n            class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n          >\n            Quantity\n          </label>\n          <input\n            type=\"number\"\n            id=\"modeOfPayment\"\n            class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n            v-model=\"this.menu.quantity\"\n            v-bind:readonly=\"\n              this.recentOrders.editPrintedInvoice === 1 &&\n              this.auth.removeTableOrderItem === 0\n            \"\n            :disabled=\"this.recentOrders.restaurantTable\"\n          />\n          <label\n            for=\"Comments\"\n            class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n          >\n            Comments\n          </label>\n          <input\n            type=\"text\"\n            id=\"Comments\"\n            class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n            v-model=\"this.menu.itemComments\"\n          />\n        </div>\n        <div class=\"flex justify-end\">\n          <button\n            @click=\"\n              this.menu.addToCartAndUpdateQty(item);\n              menu.showDialogCart = false;\n            \"\n            class=\"mt-8 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n          >\n            Add\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div\n    class=\"mt-4 w-full divide-y divide-gray-200 bg-white\"\n    v-if=\"this.menu.cart.length > 0\"\n  >\n    <details>\n      <summary class=\"question w-full cursor-pointer select-none py-3\">\n        Additional Details\n      </summary>\n      <div class=\"additional-details m-3\">\n        <label\n          for=\"invoiceNo\"\n          class=\"mt-10 block text-sm font-medium text-gray-900 dark:text-white\"\n          v-if=\"this.table.invoiceNo || invoiceData.invoiceNumber\"\n        >\n          Invoice\n        </label>\n        <input\n          class=\"invoiceNo mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n          :value=\"this.table.invoiceNo || invoiceData.invoiceNumber\"\n          v-if=\"this.table.invoiceNo || invoiceData.invoiceNumber\"\n          readonly\n        />\n        <label\n          for=\"waiter\"\n          class=\"mt-10 block text-sm font-medium text-gray-900 dark:text-white\"\n          :class=\"{ hidden: this.invoiceData.waiter === '' }\"\n        >\n          Waiter\n        </label>\n        <input\n          class=\"waiter mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n          :class=\"{ hidden: this.invoiceData.waiter === '' }\"\n          :value=\"\n            this.table.previousWaiter !== null &&\n            this.table.previousWaiter !== undefined\n              ? this.table.previousWaiter\n              : this.recentOrders.recentWaiter !== null &&\n                this.recentOrders.recentWaiter !== undefined\n              ? this.recentOrders.recentWaiter\n              : this.invoiceData.waiter\n          \"\n          readonly\n        />\n        <label\n          for=\"posProfile\"\n          class=\"mt-10 block text-sm font-medium text-gray-900 dark:text-white\"\n          :class=\"{ hidden: this.invoiceData.posProfile === '' }\"\n        >\n          POS Profile\n        </label>\n        <input\n          class=\"posProfile mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n          :class=\"{ hidden: this.invoiceData.posProfile === '' }\"\n          v-model=\"this.invoiceData.posProfile\"\n          readonly\n        />\n        <label\n          for=\"cashier\"\n          class=\"mt-10 block text-sm font-medium text-gray-900 dark:text-white\"\n          :class=\"{ hidden: this.invoiceData.cashier === '' }\"\n        >\n          Cashier\n        </label>\n        <input\n          class=\"mt-3 block w-full rounded-md border bg-gray-50 p-2.5 text-sm text-gray-900 md:w-3/5 lg:w-2/5\"\n          :class=\"{ hidden: this.invoiceData.cashier === '' }\"\n          v-model=\"this.invoiceData.cashier\"\n          readonly\n        />\n      </div>\n    </details>\n  </div>\n</template>\n\n<script>\nimport orderInfo from \"./orderInfo.vue\";\nimport { useMenuStore } from \"@/stores/Menu.js\";\nimport { useTableStore } from \"@/stores/Table.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { usetoggleRecentOrder } from \"@/stores/recentOrder.js\";\nimport { useNotifications } from \"@/stores/Notification.js\";\n\nexport default {\n  name: \"Cart\",\n  components: {\n    orderInfo,\n  },\n  methods: {\n    handleConfirmCancellation() {\n      console.log(this.invoiceData.cancelReason);\n      console.log(!this.invoiceData.cancelReason || this.invoiceData.cancelReason.trim() === '');\n      if (!this.invoiceData.cancelReason || this.invoiceData.cancelReason.trim() === '') {\n        this.notification.createNotification('Please enter a reason for cancellation');\n        return;\n      }\n      this.invoiceData.cancelInvoice();\n      this.invoiceData.cancelInvoiceFlag = false;\n    },\n  },\n  setup() {\n    const menu = useMenuStore();\n    const table = useTableStore();\n    const auth = useAuthStore();\n    const recentOrders = usetoggleRecentOrder();\n    const invoiceData = useInvoiceDataStore();\n    const notification = useNotifications();\n    return { menu, table, invoiceData, auth, recentOrders, notification };\n  },\n  mounted() {\n    window.scrollTo(0, 0);\n  },\n};\n</script>\n<style>\n.bg-gray-100 {\n  background-color: rgba(0, 0, 0, 0.2);\n}\n</style>\n"
  },
  {
    "path": "urypos/src/components/Customer.vue",
    "content": "<template>\n  <orderInfo />\n  <div class=\"container m-auto\">\n    <div class=\"mb-6 gap-6 md:grid-cols-2\">\n      <div class=\"relative mt-5 lg:mt-2\" ref=\"container\">\n        <div\n          class=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\"\n        >\n          <svg\n            aria-hidden=\"true\"\n            class=\"h-5 w-5 text-gray-500 dark:text-gray-400\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            viewBox=\"0 0 24 24\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n            ></path>\n          </svg>\n        </div>\n        <input\n          type=\"search\"\n          class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500 md:w-3/5 lg:w-2/5\"\n          placeholder=\"Search Customers\"\n          v-model=\"this.customers.search\"\n          @input=\"this.customers.handleSearchInput\"\n          @click=\"this.customers.searchCustomer()\n          \"\n          :disabled=\"this.menu.selectedOrderType === 'Aggregators' || this.recentOrders.previousOrderdCustomer !== ''\"\n          required\n        />\n\n        <div\n          v-if=\"\n            this.customers.showCustomers && this.customers.showAddNewCustomer\n          \"\n          class=\"absolute left-0 top-full z-10 max-h-64 w-full overflow-y-scroll rounded bg-white shadow md:w-3/5 lg:w-2/5\"\n          ref=\"dropdown\"\n        >\n          <div\n            class=\"h-16 rounded-lg p-4 hover:bg-gray-100\"\n            v-for=\"(customer, index) in this.customers.customer\"\n            :key=\"index\"\n            @click=\"this.customers.selectCustomer(customer)\"\n          >\n            <h1 class=\"text-base font-semibold leading-normal\">\n              {{ customer.name }}\n            </h1>\n            <h2 class=\"text-sm leading-normal\">\n              {{ customer.name }}\n              {{\n                customer.content\n                  ? this.customers.extractName(customer.content)\n                  : \"\"\n              }}\n            </h2>\n          </div>\n          <div v-if=\"this.customers.showAddNewCustomer\">\n            <div class=\"flex justify-end\">\n              <span class=\"sr-only\">Close</span>\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"mt-2 mr-2 h-4 w-4\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n                @click=\"this.customers.showAddNewCustomer = false\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  stroke-width=\"2\"\n                  d=\"M6 18L18 6M6 6l12 12\"\n                />\n              </svg>\n            </div>\n            <a\n              href=\"#\"\n              class=\"mt-1 lg:mt-0 inline-flex items-center text-blue-600 hover:underline\"\n              @click.prevent=\"\n                this.customers.newCustomerData(this.customers.search)\n              \"\n            >\n              <svg\n                fill=\"none\"\n                stroke=\"currentColor\"\n                class=\"h-8 w-8 font-extrabold\"\n                viewBox=\"0 0 25 25\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n                aria-hidden=\"true\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  d=\"M12 6v12m6-6H6\"\n                ></path>\n              </svg>\n              Create New Customer\n            </a>\n          </div>\n        </div>\n      </div>\n      <div\n        v-if=\"this.customers.showModalNewCustomer\"\n        class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n      >\n        <div class=\"mb-16 mt-10 flex items-center justify-center\">\n          <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n            <div class=\"flex justify-end\">\n              <span class=\"sr-only\">Close</span>\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"h-5 w-5\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n                @click=\"this.customers.showModalNewCustomer = false\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  stroke-width=\"2\"\n                  d=\"M6 18L18 6M6 6l12 12\"\n                />\n              </svg>\n            </div>\n\n            <h2\n              class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n            >\n              New Customer\n            </h2>\n            <label\n              for=\"newCustomer\"\n              class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n            >\n              Customer Name\n            </label>\n            <input\n              type=\"text\"\n              id=\"newCustomer\"\n              class=\"mt-4 w-full rounded-lg border border-gray-300 bg-gray-50 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n              v-model=\"this.customers.newCustomer\"\n            />\n\n            <label\n              for=\"mobileNumber\"\n              class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n            >\n              Mobile Number\n            </label>\n            <input\n              type=\"number\"\n              id=\"mobileNumber\"\n              class=\"mt-4 w-full rounded-lg border border-gray-300 bg-gray-50 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n              v-model=\"this.customers.newCustomerMobileNo\"\n            />\n            <div class=\"relative mt-5\" ref=\"container\">\n              <label\n                for=\"customerGroup\"\n                class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n              >\n                Customer Group\n              </label>\n              <input\n                type=\"text\"\n                id=\"customerGroup\"\n                class=\"mt-4 w-full rounded-lg border border-gray-300 bg-gray-50 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n                v-model=\"this.customers.customerGroup\"\n                @click=\"\n                  this.customers.showCustomersGroup = true;\n                  this.customers.pickCustomerGroup();\n                \"\n                required\n              />\n\n              <div\n                v-if=\"this.customers.showCustomersGroup\"\n                class=\"absolute left-0 top-full z-10 max-h-64 w-full overflow-y-scroll rounded bg-white shadow\"\n                ref=\"dropdown\"\n              >\n                <div\n                  class=\"h-12 rounded-lg p-4 hover:bg-gray-100\"\n                  v-for=\"(group, index) in this.customers.customerGroupList\"\n                  :key=\"index\"\n                  @click=\"this.customers.selectCustomerGroup(group)\"\n                >\n                  <h1 class=\"text-base font-semibold leading-normal\">\n                    {{ group.name }}\n                  </h1>\n                </div>\n              </div>\n            </div>\n            <div class=\"relative mt-5\" ref=\"container\">\n              <label\n                for=\"territory\"\n                class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n              >\n                Territory\n              </label>\n              <input\n                type=\"text\"\n                id=\"territory\"\n                class=\"mt-4 w-full rounded-lg border border-gray-300 bg-gray-50 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n                v-model=\"this.customers.customerTerritory\"\n                @click=\"\n                  this.customers.showCustomersTerritory = true;\n                  this.customers.pickCustomerTerritory();\n                \"\n                required\n              />\n\n              <div\n                v-if=\"this.customers.showCustomersTerritory\"\n                class=\"absolute left-0 top-full z-10 max-h-64 w-full overflow-y-scroll rounded bg-white shadow\"\n                ref=\"dropdown\"\n              >\n                <div\n                  class=\"h-12 rounded-lg p-4 hover:bg-gray-100\"\n                  v-for=\"(territory, index) in this.customers\n                    .customerTerritoryList\"\n                  :key=\"index\"\n                  @click=\"this.customers.selectCustomerTerritory(territory)\"\n                >\n                  <h1 class=\"text-base font-semibold leading-normal\">\n                    {{ territory.name }}\n                  </h1>\n                </div>\n              </div>\n            </div>\n\n            <div class=\"flex justify-end\">\n              <button\n                @click=\"this.customers.addNewCustomer()\"\n                class=\"mt-8 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n              >\n                Save\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div class=\"relative mb-4 mt-4\">\n        <div\n          class=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\"\n        >\n          <svg  \n          xmlns=\"http://www.w3.org/2000/svg\"\n          class=\"h-5 w-5\"\n          fill=\"none\"\n          viewBox=\"0 0 24 24\"\n          stroke=\"currentColor\"      \n          >\n          <path fill-rule=\"evenodd\" d=\"M5 4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V4Zm12 12V5H7v11h10Zm-5 1a1 1 0 1 0 0 2h.01a1 1 0 1 0 0-2H12Z\" clip-rule=\"evenodd\"/>\n          </svg>\n\n        </div>\n        <input\n          type=\"number\"\n          id=\"mobileNumber\"\n          class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500 md:w-3/5 lg:w-2/5\"\n          placeholder=\"Mobile Number\"\n          readonly\n          :value=\"this.customers.newCustomerMobileNo || this.recentOrders.mobileNumber || this.table.mobileNumber\"\n        />\n      </div>\n      <div class=\"relative mb-4 mt-4\" v-if=\"!this.auth.cashier\">\n        <div\n          class=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\"\n        >\n          <svg\n            class=\"h-6 w-6 text-gray-500 group-hover:text-blue-600 dark:text-gray-400 dark:group-hover:text-blue-500\"\n            fill=\"currentColor\"\n            viewBox=\"0 0 20 20\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            aria-hidden=\"true\"\n          >\n            <path\n              fill-rule=\"evenodd\"\n              d=\"M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z\"\n              clip-rule=\"evenodd\"\n            ></path>\n          </svg>\n        </div>\n        <input\n          type=\"number\"\n          id=\"numberOfPax\"\n          class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500 md:w-3/5 lg:w-2/5\"\n          placeholder=\"Pax\"\n          required\n          v-model=\"this.customers.numberOfPax\"\n          @input=\"this.customers.validateInput\"\n        />\n      </div>\n      <div class=\"relative mt-5\" ref=\"container\" v-if=\"this.auth.cashier\">\n        <div\n          class=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\"\n        >\n          <svg\n            class=\"h-6 w-6 text-gray-800 group-hover:text-blue-600 dark:text-gray-400 dark:group-hover:text-blue-500\"\n            aria-hidden=\"true\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n          >\n            <path\n              stroke=\"currentColor\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              d=\"M15 4h3c.6 0 1 .4 1 1v15c0 .6-.4 1-1 1H6a1 1 0 0 1-1-1V5c0-.6.4-1 1-1h3m0 3h6m-3 5h3m-6 0h0m3 4h3m-6 0h0m1-13v4h4V3h-4Z\"\n            />\n          </svg>\n        </div>\n        <input\n          type=\"text\"\n          class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500 md:w-3/5 lg:w-2/5\"\n          placeholder=\"Order Type\"\n          :value=\"\n            this.menu.selectedOrderType || this.recentOrders.pastOrderType\n          \"\n          @click=\"\n                this.invoiceData.editOrderType && (this.recentOrders.pastOrderType === 'Take Away' || this.recentOrders.pastOrderType === 'Delivery')\n                  ? this.customers.editOrderType(this.recentOrders.pastOrderType)\n                  : ''\n              \"\n          :readonly=\"!(this.recentOrders.pastOrderType === 'Take Away' || this.recentOrders.pastOrderType === 'Delivery')\"        \n          required\n        />\n        <div\n          v-if=\"\n             this.invoiceData.editOrderType && this.customers.showEditOrderType\n          \"\n          class=\"absolute left-0 top-full z-10 max-h-64 w-full rounded bg-white shadow md:w-3/5 lg:w-2/5\"\n          ref=\"dropdown\"\n        >\n          <div\n            class=\"h-10 mb-4 rounded-lg p-4 hover:bg-gray-100\"\n            @click=\"this.customers.selecetOrderType(customers.newOrderType)\"\n          >\n            <h2 class=\"text-sm leading-normal\">\n               {{ customers.newOrderType }}\n            </h2>\n          </div>\n        </div>\n      </div>\n\n      <h1\n        class=\"tex mt-5 text-lg font-medium\"\n        v-if=\"this.customers.customerFavouriteItems.length > 0\"\n      >\n        Favourite Items\n      </h1>\n\n      <div\n        class=\"cart-item-details mt-1 grid grid-cols-2 gap-6 py-2 sm:w-full md:w-full lg:w-full lg:grid-cols-4\"\n        v-if=\"this.customers.customerFavouriteItems.length > 0\"\n      >\n        <h3 class=\"text-base font-medium\">Item Name</h3>\n        <h3 class=\"text-center text-base font-medium\">Quantity</h3>\n      </div>\n      <div\n        v-for=\"(item, index) in this.customers.customerFavouriteItems\"\n        :key=\"index\"\n      >\n        <div\n          class=\"cart-item-details sm:min-w-none grid w-full grid-cols-2 gap-6 py-2 sm:w-full md:w-full lg:w-full lg:grid-cols-4\"\n        >\n          <span>{{ item.item_name }}</span>\n\n          <span class=\"text-center\">{{ item.qty }}</span>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport orderInfo from \"./orderInfo.vue\";\nimport { useCustomerStore } from \"@/stores/Customer.js\";\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { usetoggleRecentOrder } from \"@/stores/recentOrder.js\";\nimport { useMenuStore } from \"@/stores/Menu.js\";\nimport { useTableStore } from \"@/stores/Table.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\n\n\nexport default {\n  name: \"Customer\",\n  components: {\n    orderInfo,\n  },\n  setup() {\n    const customers = useCustomerStore();\n    const auth = useAuthStore();\n    const recentOrders = usetoggleRecentOrder();\n    const menu = useMenuStore();\n    const invoiceData = useInvoiceDataStore();\n    const table=useTableStore();\n    return { table,customers, auth, recentOrders,menu,invoiceData };\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/Header.vue",
    "content": "<template>\n  <div >\n    <nav\n      class=\"fixed left-0 top-0 z-20 w-full border-b border-gray-200 bg-white dark:border-gray-600 dark:bg-gray-900\"\n    >\n      <div\n        class=\"mx-auto flex max-w-screen-2xl items-center justify-between p-4\"\n      >\n        <!-- Logo/Title Section -->\n        <div class=\"flex items-center\">\n          <template\n            v-if=\"\n              this.tabClick.currentTab === '/Table' ||\n              this.auth.cashier ||\n              this.tabClick.isLoginPage\n            \"\n          >\n            <a href=\"/urypos/Table\" class=\"flex-shrink-0\">\n              <img :src=\"imagePath\" alt=\"URY POS logo\" class=\"w-32 lg:w-44\" />\n            </a>\n          </template>\n          <template v-else>\n            <h3\n              class=\"mb-2 mt-2 p-1 text-2xl font-medium text-gray-900 dark:text-white lg:text-3xl\"\n            >\n              {{ this.table.selectedTable }}\n            </h3>\n          </template>\n        </div>\n\n        <!-- User Menu Section -->\n        <div\n          v-if=\"!this.tabClick.isLoginPage\"\n          class=\"relative ml-4 flex-shrink-0\"\n        >\n          <button\n            type=\"button\"\n            class=\"flex items-center rounded-full bg-gray-100 p-1 text-sm hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600\"\n            id=\"user-menu-button\"\n            aria-expanded=\"false\"\n            @click=\"this.auth.toggleDropdown()\"\n            ref=\"dropdownButton\"\n          >\n            <div\n              class=\"relative inline-flex h-8 w-8 items-center justify-center overflow-hidden rounded-full bg-gray-200 dark:bg-gray-600 sm:h-8 sm:w-8\"\n            >\n              <span\n                class=\"text-sm font-medium text-gray-700 dark:text-gray-300\"\n              >\n                {{ this.auth.sessionUser.charAt(0).toUpperCase() }}\n              </span>\n            </div>\n          </button>\n\n          <!-- New Dropdown Menu Style -->\n          <div\n            v-show=\"this.auth.activeDropdown\"\n            class=\"absolute right-0 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none\"\n          >\n            <div class=\"py-1\">\n              <a\n                href=\"#\"\n                class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100\"\n              >\n                {{ this.auth.getLoginAvatar() }}\n              </a>\n              <a\n                href=\"#\"\n                class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100\"\n                @click=\"reload\"\n              >\n                Reload\n              </a>\n\n              <a\n                href=\"#\"\n                class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100\"\n                @click=\"this.auth.routeToHome()\"\n                >Switch To Desk\n              </a>\n              <div class=\"border-t border-gray-200\"></div>\n              <a\n                href=\"#\"\n                class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100\"\n                @click=\"this.auth.logOut\"\n              >\n                Log out\n              </a>\n            </div>\n          </div>\n        </div>\n      </div>\n    </nav>\n\n    <!-- Spacer for Fixed Header -->\n    <div class=\"h-16 sm:h-20\"></div>\n  </div>\n</template>\n\n<script>\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { posOpening } from \"@/stores/posOpening.js\";\nimport { posClosing } from \"@/stores/posClosing.js\";\nimport uriPosImage from \"@/assets/logos/URY_POS.jpg\";\nimport { tabFunctions } from \"@/stores/bottomTabs.js\";\nimport { useTableStore } from \"@/stores/Table.js\";\n\nexport default {\n  name: \"Header\",\n  setup() {\n    const auth = useAuthStore();\n    const posOpen = posOpening();\n    const posClose = posClosing();\n    const tabClick = tabFunctions();\n    const table = useTableStore();\n\n    return { auth, posOpen, posClose, tabClick, table };\n  },\n  data() {\n    return {\n      imagePath: uriPosImage,\n    };\n  },\n  methods: {\n    reload() {\n      window.location.reload();\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/Login.vue",
    "content": "<template>\n  <div class=\"flex flex-col items-center justify-center\">\n    <!-- Main Container -->\n    <div class=\"mt-20 w-full max-w-md\">\n      <!-- Logo Container -->\n\n      <!-- Card Container -->\n      <div class=\"rounded-lg bg-white px-6 py-8 shadow-md\">\n        <div class=\"mb-8 flex justify-center\">\n          <img\n            :src=\"imagePath\"\n            alt=\"URY POS logo\"\n            class=\"h-8 w-auto sm:h-8 lg:h-8\"\n          />\n        </div>\n\n        <form class=\"space-y-6\" @submit.prevent=\"this.auth.login\">\n          <!-- Email Input -->\n          <div class=\"relative\">\n            <div class=\"absolute inset-y-0 left-0 flex items-center pl-3\">\n              <svg\n                class=\"h-5 w-5 text-gray-400\"\n                width=\"20\"\n                height=\"20\"\n                viewBox=\"0 0 20 20\"\n                fill=\"none\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n              >\n                <path\n                  d=\"M2.5 7.65149V15.0757C2.5 15.4374 2.64367 15.7842 2.8994 16.04C3.15513 16.2957 3.50198 16.4394 3.86364 16.4394H16.1364C16.498 16.4394 16.8449 16.2957 17.1006 16.04C17.3563 15.7842 17.5 15.4374 17.5 15.0757V7.65149\"\n                  stroke=\"currentColor\"\n                  stroke-miterlimit=\"10\"\n                  stroke-linecap=\"square\"\n                />\n                <path\n                  d=\"M17.5 7.57572V5.53026C17.5 5.1686 17.3563 4.82176 17.1006 4.56603C16.8449 4.31029 16.498 4.16663 16.1364 4.16663H3.86364C3.50198 4.16663 3.15513 4.31029 2.8994 4.56603C2.64367 4.82176 2.5 5.1686 2.5 5.53026V7.57572L10 10.8333L17.5 7.57572Z\"\n                  stroke=\"currentColor\"\n                  stroke-miterlimit=\"10\"\n                  stroke-linecap=\"square\"\n                />\n              </svg>\n            </div>\n            <input\n              name=\"user_id\"\n              v-model=\"this.auth.userId\"\n              required\n              class=\"block w-full rounded-md border border-gray-200 py-3 pl-10 text-sm placeholder-gray-400 focus:border-blue-500 focus:ring-blue-500\"\n              placeholder=\"jane@example.com\"\n            />\n          </div>\n\n          <!-- Password Input -->\n          <div class=\"relative\">\n            <div class=\"absolute inset-y-0 left-0 flex items-center pl-3\">\n              <svg\n                class=\"field-icon password-icon\"\n                width=\"20\"\n                height=\"20\"\n                viewBox=\"0 0 20 20\"\n                fill=\"none\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n              >\n                <path\n                  fill-rule=\"evenodd\"\n                  clip-rule=\"evenodd\"\n                  d=\"M10.0961 1.93768H10.0264C8.94806 1.92763 7.90976 2.34591 7.13951 3.10075C6.36866 3.85619 5.9294 4.88687 5.91833 5.96612L5.91831 5.96612V5.97124V6.52695H4.3125C3.20793 6.52695 2.3125 7.42238 2.3125 8.52695V16.0165C2.3125 17.121 3.20793 18.0165 4.3125 18.0165H15.7356C16.8401 18.0165 17.7356 17.121 17.7356 16.0165V8.52695C17.7356 7.42238 16.8401 6.52695 15.7356 6.52695H14.1297V6.04576C14.1397 4.96742 13.7214 3.92913 12.9666 3.15888C12.2112 2.38803 11.1805 1.94877 10.1012 1.9377V1.93768H10.0961ZM13.1297 6.52695V6.04336V6.03838H13.1297C13.1378 5.22428 12.8222 4.44029 12.2524 3.85881C11.6831 3.27793 10.9067 2.94667 10.0934 2.93768H10.024H10.019V2.93765C9.20491 2.92955 8.42092 3.24512 7.83944 3.81497C7.25856 4.38423 6.9273 5.1607 6.91831 5.9739V6.52695H13.1297ZM4.3125 7.52695C3.76022 7.52695 3.3125 7.97467 3.3125 8.52695V16.0165C3.3125 16.5687 3.76022 17.0165 4.3125 17.0165H15.7356C16.2879 17.0165 16.7356 16.5687 16.7356 16.0165V8.52695C16.7356 7.97467 16.2879 7.52695 15.7356 7.52695H4.3125ZM10.0242 13.2384C10.5581 13.2384 10.9909 12.8056 10.9909 12.2717C10.9909 11.7377 10.5581 11.3049 10.0242 11.3049C9.49023 11.3049 9.05738 11.7377 9.05738 12.2717C9.05738 12.8056 9.49023 13.2384 10.0242 13.2384ZM11.9909 12.2717C11.9909 13.3579 11.1104 14.2384 10.0242 14.2384C8.93794 14.2384 8.05738 13.3579 8.05738 12.2717C8.05738 11.1854 8.93794 10.3049 10.0242 10.3049C11.1104 10.3049 11.9909 11.1854 11.9909 12.2717Z\"\n                  fill=\"#74808B\"\n                ></path>\n              </svg>\n            </div>\n            <input\n              :type=\"this.auth.showPassword ? 'text' : 'password'\"\n              name=\"currentPassword\"\n              v-model=\"this.auth.currentPassword\"\n              required\n              class=\"block w-full rounded-md border border-gray-200 py-3 pl-10 pr-16 text-sm placeholder-gray-400 focus:border-blue-500 focus:ring-blue-500\"\n              placeholder=\"•••••\"\n            />\n            <button\n              type=\"button\"\n              class=\"absolute inset-y-0 right-0 flex items-center pr-3\"\n              @click=\"this.auth.showPassword = !this.auth.showPassword\"\n            >\n              <span class=\"text-sm text-gray-400\">\n                {{ this.auth.showPassword ? \"Hide\" : \"Show\" }}\n              </span>\n            </button>\n          </div>\n\n          <!-- Login Button -->\n          <button\n            type=\"submit\"\n            class=\"flex w-full justify-center rounded-md border border-transparent bg-blue-600 px-4 py-3 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n          >\n            Login\n          </button>\n        </form>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport uriPosImage from \"@/assets/logos/URY_POS.jpg\";\n\nexport default {\n  setup() {\n    const auth = useAuthStore();\n    return { auth };\n  },\n  data() {\n    return {\n      imagePath: uriPosImage,\n    };\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/Menu.vue",
    "content": "<template>\n  <orderInfo />\n  <Search />\n  <div class=\"container mx-auto\" v-if=\"this.menu.paginatedItems.length > 0\">\n    <div class=\"mt-4 grid grid-cols-2 gap-4 md:grid-cols-4 lg:grid-cols-5\">\n      <div\n        class=\"rounded border px-2 py-2 text-left shadow\"\n        v-for=\"item in this.menu.paginatedItems\"\n        :key=\"item.item\"\n      >\n        <div class=\"w-full\" v-if=\"this.auth.viewItemImage\">\n          <div v-if=\"item.item_image\"\n            class=\"aspect-square w-full overflow-hidden\"\n          >\n            <img\n              :src=\"this.menu.getFullImagePath(item.item_image)\"\n              alt=\"Item Image\"\n              class=\"h-full w-full rounded object-cover\"\n            />\n          </div>\n          <div v-else class=\"relative aspect-square w-full\">\n            <img\n              :src=\"`https://dummyimage.com/640x640/f9fafa/fff&text=+`\"\n              alt=\"Item Image\"\n              class=\"h-full w-full rounded object-cover\"\n            />\n            <div class=\"absolute inset-0 flex items-center justify-center\">\n              <span class=\"text-3xl text-gray-400\">{{\n                this.menu.itemNameExtract(item.item_name)\n              }}</span>\n            </div>\n          </div>\n        </div>\n\n        <h2\n          class=\"mt-2\"\n          :class=\"{\n            'text-md overflow-hidden whitespace-nowrap text-gray-600':\n              this.auth.viewItemImage,\n            'mb-2 text-center text-lg font-normal leading-normal':\n              !this.auth.viewItemImage,\n          }\"\n        >\n          {{ item.item_name }}\n        </h2>\n        <h2\n          class=\"mt-1\"\n          :class=\"{\n            'text-sm font-bold': this.auth.viewItemImage,\n            'mb-2 mt-0 text-center text-lg font-normal leading-normal':\n              !this.auth.viewItemImage,\n          }\"\n        >\n          {{ this.invoiceData.currency }} {{ item.rate }}\n        </h2>\n        <div v-if=\"!item.qty\" class=\"text-center\">\n          <button\n            @click=\"\n              item.showInput = true;\n              this.menu.addToCart(item);\n            \"\n            class=\"mt-2 rounded border px-10 pb-2 pt-2.5 text-xs font-medium leading-normal\"\n          >\n            ADD +\n          </button>\n        </div>\n        <div v-if=\"item.qty\" class=\"mt-2 flex rounded-md text-center\">\n          <button\n            type=\"button\"\n            class=\"inline-flex items-center justify-center gap-2 border bg-white px-4 py-3 align-middle text-sm font-medium shadow-sm transition-all focus:outline-none dark:border-gray-700\"\n            :class=\"{\n              'text-gray-700':\n                this.recentOrders.editPrintedInvoice === 0 ||\n                this.auth.removeTableOrderItem === 1,\n              'text-gray-300':\n                this.recentOrders.editPrintedInvoice === 1 ||\n                this.auth.removeTableOrderItem === 0,\n            }\"\n            :disabled=\"this.recentOrders.restaurantTable\"\n            @click=\"\n              (this.recentOrders.editPrintedInvoice === 0 ||\n                this.auth.removeTableOrderItem === 1) &&\n                this.menu.decrementItemQuantity(item)\n            \"\n          >\n            -\n          </button>\n          <input\n            type=\"number\"\n            id=\"qty_input\"\n            name=\"qty_input\"\n            class=\"block w-full border border-gray-200 text-center text-sm shadow-sm\"\n            :value=\"item.qty\"\n            @input=\"item.qty = $event.target.value\"\n            readonly\n            @click=\"this.menu.showModal(item)\"\n          />\n          <button\n            type=\"button\"\n            class=\"-ml-px inline-flex items-center justify-center gap-2 border bg-white px-4 py-3 align-middle text-sm font-medium text-gray-700 shadow-sm transition-all focus:outline-none dark:border-gray-700\"\n            @click=\"this.menu.incrementItemQuantity(item)\"\n          >\n            +\n          </button>\n        </div>\n      </div>\n      <div\n        v-if=\"menu.showDialog\"\n        class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n      >\n        <div class=\"mt-10 flex items-center justify-center\">\n          <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n            <div class=\"flex justify-end\">\n              <span class=\"sr-only\">Close</span>\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"h-5 w-5\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n                @click=\"menu.showDialog = false\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  stroke-width=\"2\"\n                  d=\"M6 18L18 6M6 6l12 12\"\n                />\n              </svg>\n            </div>\n\n            <h2\n              class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n            >\n              Enter Details\n            </h2>\n            <div class=\"relative\">\n              <label\n                for=\"quantity\"\n                class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n              >\n                Quantity\n              </label>\n              <input\n                type=\"number\"\n                id=\"quantity\"\n                class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n                v-model=\"this.menu.quantity\"\n                v-bind:readonly=\"\n                  this.recentOrders.editPrintedInvoice === 1 &&\n                  this.auth.removeTableOrderItem === 0\n                \"\n                :disabled=\"this.recentOrders.restaurantTable\"\n              />\n              <label\n                for=\"comments\"\n                class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n              >\n                Comments\n              </label>\n              <input\n                type=\"text\"\n                id=\"Comments\"\n                class=\"mt-4 w-full rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n                v-model=\"this.menu.itemComments\"\n              />\n            </div>\n            <div class=\"flex justify-end\">\n              <button\n                @click=\"this.menu.addToCartAndUpdateQty(item)\"\n                class=\"mt-8 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n              >\n                Add\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <div v-else>\n    <div\n      v-if=\"this.menu.items.length === 0\"\n      class=\"flex h-screen items-center justify-center\"\n    >\n      <div class=\"text-center\">\n        Items Not Found. Please select a table or set an active menu.\n      </div>\n    </div>\n    <div v-else class=\"flex h-screen items-center justify-center\">\n      <div class=\"text-center\">Items Not Found.</div>\n    </div>\n  </div>\n  <div\n    class=\"mt-4 flex justify-center\"\n    v-if=\"this.menu.paginatedItems.length > 0\"\n  >\n    <button\n      :class=\"{ hidden: this.menu.currentPage === 1 }\"\n      :disabled=\"this.menu.currentPage === 1\"\n      @click=\"this.menu.currentPage -= 1\"\n      class=\"mr-2 rounded-md border px-2 py-1\"\n    >\n      Previous\n    </button>\n    <div v-for=\"pageNumber in this.menu.pageNumbers\">\n      <button\n        v-if=\"\n          pageNumber === this.menu.currentPage ||\n          Math.abs(pageNumber - this.menu.currentPage) <= 2\n        \"\n        :key=\"pageNumber\"\n        @click=\"this.menu.currentPage = pageNumber\"\n        :class=\"{ 'bg-gray-200': pageNumber === this.menu.currentPage }\"\n        class=\"mr-2 rounded-md border px-2 py-1\"\n      >\n        {{ pageNumber }}\n      </button>\n      <span\n        v-else-if=\"\n          this.menu.pageNumbers.indexOf(pageNumber) === 0 ||\n          this.menu.pageNumbers.indexOf(pageNumber) ===\n            this.menu.pageNumbers.length - 1\n        \"\n      >\n        ...\n      </span>\n    </div>\n    <button\n      :disabled=\"this.menu.currentPage === this.menu.totalPages\"\n      @click=\"this.menu.currentPage += 1\"\n      :class=\"{ hidden: this.menu.currentPage === this.menu.totalPages }\"\n      class=\"rounded-md border px-2 py-1\"\n    >\n      Next\n    </button>\n  </div>\n</template>\n\n<script>\nimport Search from \"./Search.vue\";\nimport orderInfo from \"./orderInfo.vue\";\nimport frappe from \"@/stores/frappeSdk.js\";\nimport { useMenuStore } from \"@/stores/Menu.js\";\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { usetoggleRecentOrder } from \"@/stores/recentOrder.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\n\nexport default {\n  data() {\n    return {\n      frappe: frappe,\n    };\n  },\n  setup() {\n    const menu = useMenuStore();\n    const auth = useAuthStore();\n    const recentOrders = usetoggleRecentOrder();\n    const invoiceData = useInvoiceDataStore();\n    return { menu, auth, recentOrders, invoiceData };\n  },\n  name: \"Menu\",\n  components: {\n    Search,\n    orderInfo,\n  },\n  mounted() {\n    window.scrollTo(0, 0);\n  },\n};\n</script>\n<style>\n.bg-gray-100 {\n  background-color: rgba(0, 0, 0, 0.2);\n}\n</style>"
  },
  {
    "path": "urypos/src/components/NotificationModal.vue",
    "content": "<template>\n  <Teleport to=\"body\">\n    <div\n      v-if=\"modal.isOpen\"\n      class=\"font-inter fixed inset-0 z-50 overflow-y-auto bg-black/25\"\n    >\n      <div class=\"flex justify-center px-4 pt-16\">\n        <!-- Modal Content -->\n        <div class=\"relative w-[576px] rounded-lg bg-white shadow-lg\">\n          <!-- Modal Header -->\n          <div class=\"border-b border-b-gray-200 px-6 py-4\">\n            <div class=\"text-lg font-medium\">{{ modal.title }}</div>\n          </div>\n\n          <!-- Modal Body -->\n          <div class=\"p-5 px-6 pb-5\">\n            <p class=\"whitespace-pre-line text-black\">\n              {{ modal.message }}\n            </p>\n          </div>\n\n          <!-- Modal Footer -->\n          <div\n            class=\"flex justify-end gap-2 rounded-b-md border-t border-t-gray-200 px-6 py-3\"\n          >\n            <button\n              v-if=\"modal.showCancelButton\"\n              @click=\"modal.handleCancel\"\n              class=\"rounded border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-50\"\n            >\n              No\n            </button>\n            <button\n              @click=\"modal.handleConfirm\"\n              class=\"rounded bg-blue-700 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-800\"\n            >\n              Yes\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n  </Teleport>\n</template>\n\n<script>\nimport { defineComponent } from \"vue\";\nimport { useNotificationModal } from \"../stores/NotificationModal\";\nimport { storeToRefs } from \"pinia\";\n\nexport default defineComponent({\n  name: \"NotificationModal\",\n  setup() {\n    const modal = useNotificationModal();\n    const { isOpen, message, showCancelButton } = storeToRefs(modal);\n\n    return {\n      modal,\n      isOpen,\n      message,\n      showCancelButton,\n    };\n  },\n});\n</script>\n"
  },
  {
    "path": "urypos/src/components/Search.vue",
    "content": "<template>\n  <!-- Search & Filters -->\n  <div\n    class=\"mt-5 flex w-full flex-col justify-between gap-2 md:flex-row lg:mt-2\"\n  >\n    <div\n      class=\"relative w-full\"\n      :class=\"{\n        'md:w-full': this.menu.selectedOrderType === 'Aggregators',\n        'md:w-3/4': this.menu.selectedOrderType === 'Aggregators',\n      }\"\n    >\n      <div\n        class=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\"\n      >\n        <svg\n          aria-hidden=\"true\"\n          class=\"h-5 w-5 text-gray-500 dark:text-gray-400\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          viewBox=\"0 0 24 24\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <path\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n            stroke-width=\"2\"\n            d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n          ></path>\n        </svg>\n      </div>\n      <input\n        type=\"search\"\n        id=\"default-search\"\n        class=\"block w-full rounded border border-gray-300 bg-gray-50 px-10 pb-2 pt-2.5 text-sm font-medium text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        placeholder=\"Item Search\"\n        v-model=\"this.menu.searchTerm\"\n        @input=\"this.menu.handleSearchInput\"\n        @click=\"this.menu.clearSearch\"\n        autocapitalize=\"none\"\n      />\n    </div>\n    <div\n      class=\"flex gap-2\"\n      v-if=\"this.menu.selectedOrderType !== 'Aggregators'\"\n    >\n      <div class=\"relative w-full\">\n        <label\n          for=\"first\"\n          class=\"absolute z-50 -mt-2 ml-2 bg-white px-2 text-xs\"\n          >Select Course</label\n        >\n        <select\n          class=\"relative w-full rounded border border-gray-300 bg-gray-50 pt-2.5 text-sm\"\n          id=\"course\"\n          v-model=\"menu.selectedCourse\"\n          @change=\"this.menu.displayAll = false\"\n        >\n          <option\n            v-for=\"(course, index) in menu.course\"\n            :key=\"index\"\n            :value=\"course.name\"\n          >\n            {{ course.name }}\n          </option>\n        </select>\n      </div>\n      <button\n        class=\"focus:shadow-outline w-28 rounded bg-blue-700 p-2 font-bold text-white hover:bg-blue-900 focus:outline-blue-500\"\n        type=\"button\"\n        :class=\"{ 'bg-blue-900': this.menu.priority }\"\n        @click=\"this.menu.showSpecialItems\"\n      >\n        Priority\n      </button>\n      <button\n        class=\"focus:shadow-outline w-28 rounded bg-blue-700 p-2 font-bold text-white hover:bg-blue-900 focus:outline-blue-500\"\n        type=\"button\"\n        :class=\"{ 'bg-blue-900': this.menu.displayAll }\"\n        @click=\"this.menu.showAllItems\"\n      >\n        All\n      </button>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useMenuStore } from \"@/stores/Menu.js\";\nexport default {\n  setup() {\n    const menu = useMenuStore();\n    return { menu };\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/Table.vue",
    "content": "<template>\n  <div\n    class=\"grid grid-cols-2 md:grid-cols-4\"\n    :class=\"[\n      {\n        'gap-4 lg:grid-cols-6': !this.auth.cashier,\n        'lg:grid-cols-4': this.auth.cashier,\n      },\n    ]\"\n  >\n    <div class=\"relative\">\n      <label for=\"first\" class=\"absolute z-50 ml-2 mt-0.5 bg-white px-2 text-xs\"\n        >Select Room</label\n      >\n      <select\n        class=\"relative mt-2 w-full rounded border border-gray-300 bg-gray-50\"\n        :class=\"{ 'mb-3': this.auth.cashier }\"\n        id=\"room\"\n        v-model=\"table.selectedRoom\"\n        @change=\"table.handleRoomChange\"\n      >\n        <option\n          v-for=\"(room, index) in table.rooms\"\n          :key=\"index\"\n          :value=\"room.name\"\n        >\n          {{ room.name }}\n        </option>\n      </select>\n    </div>\n    <div\n      v-if=\"!this.auth.cashier\"\n      @click=\"this.table.toggleTableTypeSwitch\"\n      class=\"relative mb-3 mt-2 inline-block h-10 w-28 cursor-pointer rounded bg-blue-700\"\n    >\n      <span\n        class=\"absolute w-full py-2 text-base text-white\"\n        :class=\"this.table.tableTypeClass\"\n        >{{ this.table.tableTypeLabel }}</span\n      >\n      <div\n        :style=\"{ transform: this.table.toggleTableType }\"\n        class=\"absolute left-0 h-10 w-9 rounded border bg-white transition-transform duration-300 ease-in-out\"\n      ></div>\n    </div>\n    <div class=\"relative ml-5\" v-if=\"this.auth.cashier\">\n      <div class=\"relative\">\n        <label\n          for=\"first\"\n          class=\"absolute z-50 ml-2 mt-0.5 bg-white px-2 text-xs\"\n          >Order Type</label\n        >\n        <select\n          class=\"relative mt-2 w-full rounded border border-gray-300 bg-gray-50\"\n          :class=\"{ 'mb-3': this.auth.cashier }\"\n          id=\"room\"\n          v-model=\"menu.selectedOrderType\"\n          @change=\"menu.orderTypeSelection()\"\n          :disabled=\"recentOrders.pastOrderType !== null && recentOrders.pastOrderType !== ''\"\n        >\n          <option\n            v-for=\"(type, index) in menu.orderType\"\n            :key=\"index\"\n           >\n            {{ type.name }}\n          </option>\n        </select>\n      </div>\n    </div>\n    <div\n      class=\"relative ml-5\"\n      v-if=\"this.menu.selectedOrderType === 'Aggregators' && this.auth.cashier\"\n    >\n      <label for=\"first\" class=\"absolute z-50 ml-2 mt-0.5 bg-white px-2 text-xs\"\n        >Aggregators List</label\n      >\n      <select\n        class=\"relative mt-2 w-full rounded border border-gray-300 bg-gray-50\"\n        :class=\"{ 'mb-3': auth.cashier }\"\n        id=\"room\"\n        v-model=\"menu.selectedAggregator\"\n        @change=\"menu.handleAggregatorChange\"\n        :disabled=\"menu.cartHasValue || recentOrders.pastOrderType !== null && recentOrders.pastOrderType !== ''\"\n      >\n        <option\n          v-for=\"(aggregator, index) in menu.aggregatorList\"\n          :key=\"index\"\n          :value=\"aggregator.customer\"\n        >\n          {{ aggregator.customer }}\n        </option>\n      </select>\n    </div>\n  </div>\n  <div v-if=\"!this.table.isTakeaeay\" class=\"m-auto\">\n    <div class=\"flow-root\">\n      <div\n        class=\"fixed inset-0 z-50 flex items-center justify-center bg-gray-300 bg-opacity-50 text-lg\"\n        v-if=\"this.invoiceData.isPrinting\"\n      >\n        Printing Invoice\n      </div>\n      <div class=\"grid grid-cols-2 gap-4 md:grid-cols-4 lg:grid-cols-5\">\n        <div\n          w-full\n          class=\"w-full max-w-sm rounded border border-gray-200 bg-white shadow dark:border-gray-700 dark:bg-gray-800\"\n          v-for=\"table in auth.cashier\n            ? this.table.tables\n            : this.table.filteredTables\"\n          :key=\"table.name\"\n        >\n          <div class=\"flex justify-between\">\n            <div class=\"flex justify-start px-2 pt-2\">\n              <span\n                class=\"me-2 rounded px-2.5 py-0.5 text-sm font-medium\"\n                :class=\"{\n                  'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300':\n                    this.table.getBadgeType(table) === 'red',\n                  'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300':\n                    this.table.getBadgeType(table) === 'default',\n\n                  'bg-yellow-100 text-yellow-800':\n                    this.table.getBadgeType(table) === 'yellow',\n                  'bg-green-100 text-green-800':\n                    this.table.getBadgeType(table) === 'green',\n                }\"\n              >\n                {{ this.table.getBadgeText(table) }}\n              </span>\n            </div>\n            <div class=\"relative\" v-if=\"table.occupied === 1\">\n              <button\n                class=\"inline-block rounded p-1.5 text-sm text-gray-500 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-700\"\n                type=\"button\"\n                @click=\"this.table.toggleDropdown(table.name)\"\n              >\n                <svg\n                  class=\"h-6 w-6\"\n                  aria-hidden=\"true\"\n                  fill=\"currentColor\"\n                  viewBox=\"0 0 20 20\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                >\n                  <path\n                    d=\"M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z\"\n                  ></path>\n                </svg>\n              </button>\n              <div\n                class=\"absolute right-0 z-10 w-36 divide-y divide-gray-100 rounded bg-white shadow dark:bg-gray-700\"\n                v-show=\"this.table.activeDropdown === table.name\"\n              >\n                <ul class=\"py-2\">\n                  <li>\n                    <a\n                      href=\"#\"\n                      class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white\"\n                      @click=\"this.table.showModal = true\"\n                      >Table Transfer</a\n                    >\n                  </li>\n                  <li v-if=\"this.auth.hasAccess\">\n                    <a\n                      href=\"#\"\n                      class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white\"\n                      @click=\"this.table.showModalCaptainTransfer = true\"\n                      >Captain Transfer</a\n                    >\n                  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n          <div class=\"flex flex-col pb-4\">\n            <div\n              class=\"mt-1 text-center\"\n              @click=\"\n                table.occupied === 1 && !this.auth.restrictTableOrder\n                  ? this.table.routeToMenu(table)\n                  : ''\n              \"\n            >\n              <h5\n                class=\"mt-2 text-xl font-medium text-gray-900 dark:text-white\"\n                :class=\"{ 'mt-3': table.occupied === 0 }\"\n              >\n                {{ table.name }}\n              </h5>\n              <span class=\"text-sm text-gray-500 dark:text-gray-400\">\n                {{\n                  table.occupied === 1\n                    ? this.table.getTimeDifference(table)\n                    : \"\"\n                }}</span\n              >\n            </div>\n            <div class=\"mt-8 text-center\" v-if=\"table.occupied != 1\">\n              <button\n                type=\"button\"\n                class=\"inline-flex items-center rounded px-2 py-2.5 text-center text-sm font-medium text-white hover:bg-[#2557D6]/90 focus:outline-none focus:ring-4 focus:ring-[#2557D6]/50 dark:focus:ring-[#2557D6]/50\"\n                :class=\"[\n                  {\n                    'bg-blue-700': !this.auth.restrictTableOrder,\n                    'pointer-events-none bg-blue-400':\n                      this.auth.restrictTableOrder,\n                  },\n                ]\"\n                @click=\"\n                  !this.auth.restrictTableOrder &&\n                    this.table.addToSelectedTables(table)\n                \"\n              >\n                Open Table\n                <svg\n                  class=\"ml-2 h-6 w-6 dark:text-white\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  viewBox=\"0 0 24 24\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                >\n                  <path\n                    stroke-linecap=\"round\"\n                    stroke-linejoin=\"round\"\n                    stroke-width=\"2\"\n                    d=\"M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z\"\n                  ></path>\n                </svg>\n              </button>\n            </div>\n            <div class=\"mt-2 flex justify-center\" v-if=\"table.occupied === 1\">\n              <button\n                type=\"button\"\n                class=\"mb-2 me-2 inline-flex items-center rounded bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-[#2557D6]/90 focus:outline-none focus:ring-4 focus:ring-[#2557D6]/50 dark:focus:ring-[#2557D6]/50\"\n                @click=\"this.invoiceData.billing(table)\"\n              >\n                <svg\n                  class=\"svg-icon mr-2\"\n                  viewBox=\"0 0 24 24\"\n                  width=\"18\"\n                  height=\"18\"\n                  fill=\"white\"\n                >\n                  <path\n                    d=\"M6 19H3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h3V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v4h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1h-3v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2zm0-2v-1a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1h2V9H4v8h2zM8 4v3h8V4H8zm0 13v3h8v-3H8zm-3-7h3v2H5v-2z\"\n                  />\n                </svg>\n\n                Bill\n              </button>\n\n              <div\n                class=\"relative inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full border hover:bg-blue-700 hover:text-white focus:outline-none focus:ring-4 focus:ring-blue-300 dark:border-blue-500 dark:text-blue-500 dark:hover:bg-blue-500 dark:hover:text-white dark:focus:ring-blue-800\"\n                :class=\"[\n                  {\n                    'border-blue-700 text-blue-700':\n                      !this.auth.restrictTableOrder,\n                    'pointer-events-none border-blue-400 text-blue-400':\n                      this.auth.restrictTableOrder,\n                  },\n                ]\"\n                @click=\"\n                  !this.auth.restrictTableOrder && this.table.routeToCart(table)\n                \"\n              >\n                <svg\n                  aria-hidden=\"true\"\n                  class=\"h-10 w-6\"\n                  fill=\"currentColor\"\n                  viewBox=\"0 0 20 20\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                >\n                  <path d=\"M10 12a2 2 0 100-4 2 2 0 000 4z\"></path>\n                  <path\n                    fill-rule=\"evenodd\"\n                    d=\"M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z\"\n                    clip-rule=\"evenodd\"\n                  ></path>\n                </svg>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n    <div\n      v-if=\"this.table.tables.length === 0\"\n      class=\"inset-0 mt-72 flex items-center justify-center\"\n    >\n      <div class=\"text-center\">\n        Tables not found. Please set tables for the room\n        <span class=\"font-medium\">{{ this.table.selectedRoom }}.</span>\n      </div>\n    </div>\n  </div>\n  <div v-else>\n    <takeAwayTable />\n  </div>\n\n  <div\n    v-if=\"table.showModal\"\n    class=\"fixed inset-0 z-10 overflow-y-auto bg-gray-100\"\n  >\n    <div class=\"mt-20 flex items-center justify-center\">\n      <div class=\"mt-10 w-full rounded bg-white p-6 shadow-lg md:max-w-md\">\n        <div class=\"flex justify-end\">\n          <span class=\"sr-only\">Close</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"h-5 w-5\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            @click=\"this.table.showModal = false\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M6 18L18 6M6 6l12 12\"\n            />\n          </svg>\n        </div>\n\n        <h2\n          class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n        >\n          Table Transfer\n        </h2>\n        <div class=\"relative\" ref=\"container\">\n          <label\n            for=\"newTable\"\n            class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n          >\n            New Table\n          </label>\n          <input\n            type=\"text\"\n            class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n            v-model=\"table.newTable\"\n            @click=\"\n              this.table.showTable = true;\n              this.table.tableSearch();\n            \"\n          />\n          <div\n            v-if=\"this.table.showTable\"\n            class=\"absolute left-0 top-full z-10 max-h-64 w-full overflow-y-scroll rounded bg-white shadow\"\n            ref=\"dropdown\"\n          >\n            <div\n              class=\"h-16 w-full rounded p-4 hover:bg-gray-100\"\n              v-for=\"(tables, index) in this.table.searchTable\"\n              :key=\"index\"\n              @click=\"this.table.selectTable(tables)\"\n            >\n              <h1 class=\"text-base font-semibold leading-normal\">\n                {{ tables.name }}\n              </h1>\n            </div>\n          </div>\n        </div>\n        <label\n          for=\"newTable\"\n          class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n        >\n          Current Table\n        </label>\n        <input\n          type=\"text\"\n          id=\"newTable\"\n          class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n          :value=\"table.tableName\"\n          readonly\n        />\n        <div class=\"flex justify-end\">\n          <button\n            @click=\"\n              this.table.showModal = false;\n              this.table.tableTransfer(table);\n            \"\n            class=\"mt-8 rounded bg-blue-700 px-3 py-2 text-white hover:bg-blue-600\"\n          >\n            Transfer\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div\n    v-if=\"table.showModalCaptainTransfer\"\n    class=\"fixed inset-0 z-10 overflow-y-auto bg-gray-100\"\n  >\n    <div class=\"mt-20 flex items-center justify-center\">\n      <div class=\"mt-10 w-full rounded bg-white p-6 shadow-lg md:max-w-md\">\n        <div class=\"flex justify-end\">\n          <span class=\"sr-only\">Close</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"h-5 w-5\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            @click=\"table.showModalCaptainTransfer = false\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M6 18L18 6M6 6l12 12\"\n            />\n          </svg>\n        </div>\n        <h2\n          class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n        >\n          Captain Transfer\n        </h2>\n        <div class=\"relative\" ref=\"container\">\n          <label\n            for=\"newTable\"\n            class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n          >\n            New Captain\n          </label>\n          <input\n            type=\"text\"\n            class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n            @click=\"\n              this.table.showCaptain = true;\n              this.table.fetchCaptain();\n            \"\n            v-model=\"this.table.newCaptain\"\n          />\n          <div\n            v-if=\"this.table.showCaptain\"\n            class=\"absolute left-0 top-full z-10 max-h-64 w-full overflow-y-scroll rounded bg-white shadow\"\n            ref=\"dropdown\"\n          >\n            <div\n              class=\"h-16 w-full rounded p-4 hover:bg-gray-100\"\n              v-for=\"(captain, index) in this.table.searchCaptian\"\n              :key=\"index\"\n              @click=\"this.table.selectcaptain(captain)\"\n            >\n              <h1 class=\"text-base font-semibold leading-normal\">\n                {{ captain.name }}\n              </h1>\n            </div>\n          </div>\n        </div>\n        <label\n          for=\"newTable\"\n          class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n        >\n          Current Captain\n        </label>\n        <input\n          type=\"text\"\n          id=\"newTable\"\n          class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n          :value=\"this.table.currentCaptain\"\n          readonly\n        />\n        <div class=\"flex justify-end\">\n          <button\n            @click=\"\n              this.table.showModalCaptainTransfer = false;\n              this.table.captianTransfer();\n            \"\n            class=\"mt-8 rounded bg-blue-700 px-3 py-2 text-white hover:bg-blue-600\"\n          >\n            Transfer\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useTableStore } from \"@/stores/Table.js\";\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { useMenuStore } from \"@/stores/Menu.js\";\nimport takeAwayTable from \"./takeAwayTable.vue\";\nimport { usetoggleRecentOrder } from \"@/stores/recentOrder.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\n\nexport default {\n  name: \"Table\",\n  components: {\n    takeAwayTable,\n  },\n  setup() {\n    const table = useTableStore();\n    const invoiceData = useInvoiceDataStore();\n    const auth = useAuthStore();\n    const menu = useMenuStore();\n    const recentOrders = usetoggleRecentOrder();\n    \n    return { table, invoiceData, auth, menu,recentOrders };\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/bottomTabs.vue",
    "content": "<template>\n  <div\n    class=\"fixed bottom-0 left-0 z-50 h-16 w-full border-t border-gray-200 bg-white dark:border-gray-600 dark:bg-gray-700\"\n    v-if=\"!this.tabClick.isLoginPage\"\n    id=\"tab\"\n  >\n    <div\n      class=\"mx-auto grid h-full max-w-lg font-medium\"\n      :class=\"[\n        {\n          'grid-cols-4': !auth.cashier,\n          'grid-cols-5': auth.cashier,\n        },\n      ]\"\n    >\n      <router-link\n        :to=\"invoiceData.invoiceUpdating ? '#' : '/Table'\"\n        class=\"group inline-flex flex-col items-center justify-center border-x border-gray-200 px-5 hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800\"\n      >\n        <svg\n          class=\"h-6 w-6\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Table',\n              'text-blue-600': this.tabClick.currentTab === '/Table',\n            },\n          ]\"\n          fill=\"currentColor\"\n          viewBox=\"0 0 20 20\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          aria-hidden=\"true\"\n        >\n          <path\n            d=\"M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n          ></path>\n        </svg>\n\n        <span\n          class=\"text-sm\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Table',\n              'text-blue-600': this.tabClick.currentTab === '/Table',\n            },\n          ]\"\n          >Table</span\n        >\n      </router-link>\n\n      <router-link\n        :to=\"invoiceData.invoiceUpdating ? '#' : '/Menu'\"\n        class=\"group inline-flex flex-col items-center justify-center border-r border-gray-200 px-5 hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800\"\n        @click=\"this.tabClick.clickMenuTab()\"\n      >\n        <svg\n          class=\"h-6 w-6\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Menu',\n              'text-blue-600': this.tabClick.currentTab === '/Menu',\n            },\n          ]\"\n          fill=\"currentColor\"\n          viewBox=\"0 0 20 20\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <path d=\"M9 2a1 1 0 000 2h2a1 1 0 100-2H9z\"></path>\n          <path\n            fill-rule=\"evenodd\"\n            d=\"M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z\"\n            clip-rule=\"evenodd\"\n          ></path>\n        </svg>\n\n        <span\n          class=\"text-sm\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Menu',\n              'text-blue-600': this.tabClick.currentTab === '/Menu',\n            },\n          ]\"\n          >Menu</span\n        ></router-link\n      >\n      <router-link\n        :to=\"invoiceData.invoiceUpdating ? '#' : '/Customer'\"\n        class=\"group inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800\"\n        @click=\"!this.auth.cashier && this.tabClick.checkActiveTable()\"\n      >\n        <svg\n          class=\"h-6 w-6\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Customer',\n              'text-blue-600': this.tabClick.currentTab === '/Customer',\n            },\n          ]\"\n          fill=\"currentColor\"\n          viewBox=\"0 0 20 20\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          aria-hidden=\"true\"\n        >\n          <path\n            fill-rule=\"evenodd\"\n            d=\"M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z\"\n            clip-rule=\"evenodd\"\n          ></path>\n        </svg>\n\n        <span\n          class=\"text-sm\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Customer',\n              'text-blue-600': this.tabClick.currentTab === '/Customer',\n            },\n          ]\"\n          >Customer</span\n        ></router-link\n      >\n      <router-link\n        to=\"/Cart\"\n        class=\"group inline-flex flex-col items-center justify-center border-x border-gray-200 px-5 hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800\"\n        @click=\"!this.auth.cashier && this.tabClick.checkActiveTable()\"\n      >\n        <svg\n          aria-hidden=\"true\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Cart',\n              'text-blue-600': this.tabClick.currentTab === '/Cart',\n            },\n          ]\"\n          class=\"h-6 w-6 group-hover:text-blue-600 dark:text-gray-400 dark:group-hover:text-blue-500\"\n          fill=\"currentColor\"\n          viewBox=\"0 0 20 20\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <path\n            d=\"M3 1a1 1 0 000 2h1.22l.305 1.222a.997.997 0 00.01.042l1.358 5.43-.893.892C3.74 11.846 4.632 14 6.414 14H15a1 1 0 000-2H6.414l1-1H14a1 1 0 00.894-.553l3-6A1 1 0 0017 3H6.28l-.31-1.243A1 1 0 005 1H3zM16 16.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM6.5 18a1.5 1.5 0 100-3 1.5 1.5 0 000 3z\"\n          ></path>\n        </svg>\n\n        <span\n          class=\"text-sm group-hover:text-blue-600 dark:text-gray-400 dark:group-hover:text-blue-500\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/Cart',\n              'text-blue-600': this.tabClick.currentTab === '/Cart',\n            },\n          ]\"\n          >Cart</span\n        >\n      </router-link>\n      <router-link\n        :to=\"invoiceData.invoiceUpdating ? '#' : '/recentOrder'\"\n        v-if=\"this.auth.cashier\"\n        class=\"group inline-flex flex-col items-center justify-center border-x border-gray-200 px-5 hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800\"\n      >\n        <svg\n          class=\"h-5 w-5\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/recentOrder',\n              'text-blue-600': this.tabClick.currentTab === '/recentOrder',\n            },\n          ]\"\n          fill=\"currentColor\"\n          viewBox=\"0 0 20 20\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          aria-hidden=\"true\"\n        >\n          <path\n            d=\"M14.066 0H7v5a2 2 0 0 1-2 2H0v11a1.97 1.97 0 0 0 1.934 2h12.132A1.97 1.97 0 0 0 16 18V2a1.97 1.97 0 0 0-1.934-2Zm-3 15H4.828a1 1 0 0 1 0-2h6.238a1 1 0 0 1 0 2Zm0-4H4.828a1 1 0 0 1 0-2h6.238a1 1 0 1 1 0 2Z\"\n          />\n          <path\n            d=\"M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.98 2.98 0 0 0 .13 5H5Z\"\n          />\n        </svg>\n\n        <span\n          class=\"text-sm\"\n          :class=\"[\n            {\n              'text-gray-500': this.tabClick.currentTab !== '/recentOrder',\n              'text-blue-600': this.tabClick.currentTab === '/recentOrder',\n            },\n          ]\"\n          >OrderLog</span\n        >\n      </router-link>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { tabFunctions } from \"@/stores/bottomTabs.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\nexport default {\n  name: \"Bottom Tabs\",\n  setup() {\n    const auth = useAuthStore();\n    const invoiceData = useInvoiceDataStore();\n    const tabClick = tabFunctions();\n    return { auth, tabClick, invoiceData };\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/orderInfo.vue",
    "content": "<template>\n  <nav class=\"mb-3 mt-1 flex\" aria-label=\"Breadcrumb\" v-if=\"this.auth.cashier\">\n    <ol\n      class=\"inline-flex items-center space-x-1 rtl:space-x-reverse md:space-x-2\"\n    >\n      <li>\n        <div class=\"flex items-center\">\n          <span class=\"text-base font-medium text-gray-700 dark:text-gray-400\"\n            >Order</span\n          >\n        </div>\n      </li>\n      <li aria-current=\"page\">\n        <div class=\"flex items-center\">\n          <svg\n            class=\"mx-1 h-3 w-3 text-gray-400 rtl:rotate-180\"\n            aria-hidden=\"true\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            fill=\"none\"\n            viewBox=\"0 0 6 10\"\n          >\n            <path\n              stroke=\"currentColor\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"m1 9 4-4-4-4\"\n            />\n          </svg>\n          <span\n            class=\"ms-1 text-base font-medium text-gray-500 dark:text-gray-400 md:ms-2\"\n            >{{ this.recentOrders.orderNumber }}</span\n          >\n        </div>\n      </li>\n    </ol>\n  </nav>\n</template>\n<script>\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { usetoggleRecentOrder } from \"@/stores/recentOrder.js\";\nexport default {\n  setup() {\n    const auth = useAuthStore();\n    const recentOrders = usetoggleRecentOrder();\n    return { auth, recentOrders };\n  },\n  name: \"orderInfo\",\n};\n</script>\n"
  },
  {
    "path": "urypos/src/components/posClosing.vue",
    "content": "<template>\n  <div class=\"mt-10 flex items-center justify-between\">\n    <div class=\"flex items-center\">\n      <h3 class=\"mr-3 text-lg font-semibold text-gray-900 dark:text-white\">\n        POS Closing Entry\n      </h3>\n      <span\n        class=\"me-2 rounded px-2.5 py-0.5 text-sm font-medium\"\n        :class=\"{\n          'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300':\n            this.posClose.getBadgeType() === 'red',\n          'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300':\n            this.posClose.getBadgeType() === 'default',\n\n          'bg-yellow-100 text-yellow-800':\n            this.posClose.getBadgeType() === 'yellow',\n        }\"\n      >\n        <span class=\"text-xs\">{{ this.posClose.getBadgeText() }}</span>\n      </span>\n    </div>\n    <div class=\"flex space-x-4\">\n      <button\n        @click=\"this.posClose.savePosClosing()\"\n        class=\"rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:outline-none\"\n        v-if=\"this.posClose.posClosing\"\n      >\n        Save\n      </button>\n      <button\n        v-if=\"this.posClose.posCloseSaved\"\n        @click=\"this.posClose.showSumbitPosCloseModal()\"\n        class=\"rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:outline-none\"\n      >\n        Submit\n      </button>\n    </div>\n  </div>\n  <h3 class=\"text-base font-normal text-gray-900 dark:text-white\">\n    Period Details\n  </h3>\n\n  <div class=\"mb-6 mt-6 grid gap-6 md:grid-cols-2\">\n    <div>\n      <label\n        for=\"startDate\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Period Start Date</label\n      >\n      <input\n        v-model=\"this.posClose.startDate\"\n        readonly\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        type=\"text\"\n      />\n    </div>\n    <div>\n      <label\n        for=\"postingDate\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n      >\n        <label\n          for=\"postingDate\"\n          class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n          >Posting Date</label\n        >\n      </label>\n      <input\n        v-model=\"this.posClose.postingDate\"\n        readonly\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        type=\"text\"\n      />\n    </div>\n    <div>\n      <label\n        for=\"endDate\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Period End Date</label\n      >\n\n      <date-picker\n        v-model:value=\"this.posClose.periodEndDate\"\n        :default-value=\"new Date()\"\n        class=\"my-custom-date-picker\"\n        type=\"datetime\"\n      ></date-picker>\n    </div>\n    <div>\n      <label\n        for=\"postingTime\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Posting Time</label\n      >\n      <date-picker\n        v-model:value=\"this.posClose.postingTime\"\n        :default-value=\"this.posClose.postingTime\"\n        type=\"time\"\n        class=\"my-custom-time-picker\"\n      ></date-picker>\n    </div>\n\n    <div class=\"mb-6 gap-6 md:grid-cols-2\">\n      <div class=\"relative\" ref=\"container\">\n        <label\n          for=\"posOpen\"\n          class=\"block text-sm font-medium text-gray-900 dark:text-white\"\n        >\n          POS Opening Entry\n        </label>\n        <input\n          type=\"text\"\n          id=\"posOpen\"\n          class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          v-model=\"this.posClose.selectedPosOpenEntry\"\n          @click=\"this.posClose.selectPosOpen()\"\n          required\n        />\n        <div\n          v-if=\"this.posClose.showPosOpen\"\n          class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          ref=\"dropdown\"\n        >\n          <div\n            class=\"h-10 rounded-lg p-2 hover:bg-gray-100\"\n            v-for=\"(posOpen, index) in this.posClose.posOpenEntries\"\n            :key=\"index\"\n            @click=\"this.posClose.selectPos(posOpen)\"\n          >\n            <h1 class=\"text-base font-medium leading-normal\">\n              {{ posOpen.name }}\n            </h1>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <hr class=\"my-6 border-t border-gray-300\" />\n\n  <h3 class=\"text-base font-semibold text-gray-900 dark:text-white\">\n    User Details\n  </h3>\n  <div class=\"mb-6 mt-5 grid gap-6 md:grid-cols-2\">\n    <div class=\"md:col-span-1\">\n      <label\n        for=\"company\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n      >\n        Company\n      </label>\n      <input\n        type=\"text\"\n        id=\"company\"\n        v-model=\"this.invoiceData.company\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        required\n      />\n    </div>\n    <div class=\"flex flex-col justify-between md:col-span-1\">\n      <div class=\"mb-6\">\n        <label\n          for=\"posProfile\"\n          class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n          >POS Profile</label\n        >\n        <input\n          type=\"text\"\n          id=\"posProfile\"\n          class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          v-model=\"this.invoiceData.posProfile\"\n          required\n        />\n      </div>\n      <div>\n        <label\n          for=\"cashier\"\n          class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n          >Cashier</label\n        >\n        <input\n          type=\"text\"\n          id=\"cashier\"\n          v-model=\"this.posClose.cashier\"\n          class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          required\n        />\n      </div>\n    </div>\n  </div>\n  <div v-if=\"this.posClose.openingBalance.length > 0\">\n    <hr class=\"my-6 border-t border-gray-300\" />\n    <h3 class=\"mb-3 text-base font-semibold text-gray-900 dark:text-white\">\n      Modes of Payment\n    </h3>\n\n    <h3 class=\"mb-3 text-sm font-normal text-gray-900 dark:text-white\">\n      Payment Reconciliation\n    </h3>\n\n    <div class=\"relative overflow-x-auto shadow-md sm:rounded-lg\">\n      <table class=\"w-full text-left text-sm text-gray-500 dark:text-gray-400\">\n        <thead\n          class=\"bg-gray-50 text-base font-semibold uppercase text-gray-900 dark:text-white\"\n        >\n          <tr>\n            <th scope=\"col\" class=\"px-6 py-3\">Mode of Payment</th>\n            <th scope=\"col\" class=\"px-6 py-3 text-center\">Opening Amount</th>\n            <th scope=\"col\" class=\"px-6 py-3 text-center\">Closing Amount</th>\n            <th scope=\"col\" class=\"px-6 py-3\"></th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr\n            class=\"border-b bg-white dark:border-gray-700 dark:bg-gray-900\"\n            v-for=\"(modeOfPayment, index) in posClose.openingBalance\"\n            :key=\"index\"\n          >\n            <th\n              scope=\"row\"\n              class=\"whitespace-nowrap px-6 py-4 font-medium text-gray-900 dark:text-white\"\n            >\n              {{ modeOfPayment.mode_of_payment }}\n            </th>\n            <td\n              class=\"px-6 py-4 text-center font-medium text-gray-900 dark:text-white\"\n            >\n              <input\n                type=\"number\"\n                id=\"amount\"\n                name=\"amount\"\n                v-model=\"modeOfPayment.opening_amount\"\n                class=\"border-none text-center\"\n              />\n            </td>\n            <td\n              class=\"px-6 py-4 text-center font-medium text-gray-900 dark:text-white\"\n            >\n              <input\n                type=\"number\"\n                id=\"amount\"\n                name=\"amount\"\n                v-model=\"this.posClose.closingAmount\"\n                class=\"border-none text-center\"\n              />\n            </td>\n\n            <td class=\"px-6 py-4\">\n              <button\n                class=\"p-2 text-center\"\n                type=\"button\"\n                @click=\"this.posClose.deleteRow(index)\"\n              >\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  width=\"25\"\n                  height=\"25\"\n                  fill=\"currentColor border\"\n                  class=\"bi bi-trash\"\n                  viewBox=\"0 0 16 16\"\n                >\n                  <path\n                    d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z\"\n                  ></path>\n                  <path\n                    d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z\"\n                  ></path>\n                </svg>\n              </button>\n            </td>\n          </tr>\n        </tbody>\n      </table>\n    </div>\n  </div>\n  <hr class=\"my-6 border-t border-gray-300\" />\n\n  <h3 class=\"text-base font-semibold text-gray-900 dark:text-white\">Totals</h3>\n  <div class=\"mb-6 mt-6 grid gap-6 md:grid-cols-2\">\n    <div>\n      <label\n        for=\"grandTotal\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n      >\n        Grand Total</label\n      >\n      <input\n        type=\"text\"\n        id=\"grandTotal\"\n        v-model=\"this.posClose.grandTotal\"\n        class=\"b block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        required\n      />\n    </div>\n    <div>\n      <label\n        for=\"totalInvoices\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Total Invoices</label\n      >\n      <input\n        type=\"text\"\n        id=\"totalInvoices\"\n        v-model=\"this.posClose.totalInvoices\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        required\n      />\n    </div>\n    <div>\n      <label\n        for=\"netTotak\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Net Total</label\n      >\n      <input\n        type=\"text\"\n        id=\"netTotak\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        v-model=\"this.posClose.netTotal\"\n        required\n      />\n    </div>\n    <div>\n      <label\n        for=\"totalQty\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Total Quantity</label\n      >\n      <input\n        type=\"text\"\n        id=\"totalQty\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        v-model=\"this.posClose.totalQty\"\n        required\n      />\n    </div>\n  </div>\n  <hr class=\"my-6 border-t border-gray-300\" />\n  <div\n    v-if=\"this.posClose.showSumbitPosclose\"\n    class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n  >\n    <div class=\"mt-3 flex items-center justify-center\">\n      <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n        <div class=\"flex items-center justify-between\">\n          <h3 class=\"text-xl text-gray-900 dark:text-white\">Confirm</h3>\n          <span class=\"sr-only\">Close</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"h-5 w-5 cursor-pointer\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            @click=\"this.posClose.showSumbitPosclose = false\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M6 18L18 6M6 6l12 12\"\n            />\n          </svg>\n        </div>\n\n        <h3\n          class=\"mt-5 block text-left text-base text-gray-900 dark:text-white\"\n        >\n          Permanently Submit{{ this.posClose.posClosingEntry }}?\n        </h3>\n        <div class=\"flex justify-end\">\n          <button\n            @click=\"this.posClose.showSumbitPosclose = false\"\n            class=\"mr-3 mt-6 rounded border border-gray-300 bg-gray-50 px-3 py-2\"\n          >\n            No\n          </button>\n          <button\n            @click=\"this.posClose.sumbitPosClosing()\"\n            class=\"mt-6 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n          >\n            Yes\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\nimport { posClosing } from \"@/stores/posClosing.js\";\nimport DatePicker from \"vue-datepicker-next\";\nimport \"vue-datepicker-next/index.css\";\nimport { Badge } from \"flowbite-vue\";\n\nexport default {\n  name: \"posClose\",\n  components: { DatePicker, Badge },\n  setup() {\n    const invoiceData = useInvoiceDataStore();\n    const posClose = posClosing();\n    return { invoiceData, posClose };\n  },\n  mounted() {\n    this.posClose.setFormattedDate();\n  },\n  data() {\n    return {\n      search: \"\",\n      selectedCustomer: null,\n    };\n  },\n};\n</script>\n<style>\n.my-custom-time-picker {\n  width: 100%;\n  color: black;\n}\n</style>\n"
  },
  {
    "path": "urypos/src/components/posOpening.vue",
    "content": "<template>\n  <div class=\"mt-10 flex items-center justify-between\">\n    <div class=\"flex items-center\">\n      <h3 class=\"mr-3 text-lg font-semibold text-gray-900 dark:text-white\">\n        POS Opening Entry\n      </h3>\n      <span\n        class=\"me-2 rounded px-2.5 py-0.5 text-sm font-medium\"\n        :class=\"{\n          'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300':\n            this.posOpen.getBadgeType() === 'red',\n\n          'bg-yellow-100 text-yellow-800':\n            this.posOpen.getBadgeType() === 'yellow',\n        }\"\n      >\n        <span class=\"text-xs\">{{ this.posOpen.getBadgeText() }}</span>\n      </span>\n    </div>\n    <div class=\"flex space-x-4\">\n      <button\n        @click=\"this.posOpen.savePosOpening()\"\n        class=\"rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:outline-none\"\n        v-if=\"this.posOpen.posOpencreation\"\n      >\n        Save\n      </button>\n      <button\n        v-if=\"this.posOpen.posOpenSaved\"\n        @click=\"this.posOpen.showSumbitPosOpenModal()\"\n        class=\"rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:outline-none\"\n      >\n        Submit\n      </button>\n    </div>\n  </div>\n\n  <div class=\"mb-6 mt-6 grid gap-6 md:grid-cols-2\">\n    <div>\n      <label\n        for=\"startDate\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Period Start Date</label\n      >\n      <date-picker\n        v-model:value=\"this.posOpen.startDate\"\n        :default-value=\"new Date()\"\n        class=\"my-custom-date-picker\"\n        type=\"datetime\"\n      ></date-picker>\n    </div>\n    <div>\n      <label\n        for=\"postingDate\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n      >\n        <label\n          for=\"postingDate\"\n          class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n          >Posting Date</label\n        >\n      </label>\n      <input\n        v-model=\"this.posOpen.postingDate\"\n        readonly\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        type=\"text\"\n      />\n    </div>\n  </div>\n  <hr class=\"my-6 border-t border-gray-300\" />\n  <div class=\"mb-6 mt-6 grid gap-6 md:grid-cols-2\">\n    <div>\n      <label\n        for=\"company\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Company</label\n      >\n      <input\n        type=\"text\"\n        id=\"company\"\n        v-model=\"this.invoiceData.company\"\n        class=\"b block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        required\n      />\n    </div>\n    <div>\n      <label\n        for=\"cashier\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Cashier</label\n      >\n      <input\n        type=\"text\"\n        id=\"cashier\"\n        v-model=\"this.invoiceData.cashier\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        required\n      />\n    </div>\n    <div>\n      <label\n        for=\"posProfile\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >POS Profile</label\n      >\n      <input\n        type=\"text\"\n        id=\"posProfile\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        v-model=\"this.invoiceData.posProfile\"\n        required\n      />\n    </div>\n    <div>\n      <label\n        for=\"branch\"\n        class=\"mb-2 block text-sm font-medium text-gray-900 dark:text-white\"\n        >Branch</label\n      >\n      <input\n        type=\"text\"\n        id=\"branch\"\n        class=\"block w-full rounded-md border border-gray-300 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n        v-model=\"this.invoiceData.branch\"\n        required\n      />\n    </div>\n  </div>\n  <hr class=\"my-6 border-t border-gray-300\" />\n  <h3 class=\"mb-3 text-base font-semibold text-gray-900 dark:text-white\">\n    Opening Balance Details\n  </h3>\n\n  <div class=\"relative overflow-x-auto shadow-md sm:rounded-lg\">\n    <table class=\"w-full text-left text-sm text-gray-500 dark:text-gray-400\">\n      <thead\n        class=\"bg-gray-50 text-base font-semibold uppercase text-gray-900 dark:text-white\"\n      >\n        <tr>\n          <th scope=\"col\" class=\"px-6 py-3\">Mode of Payment</th>\n          <th scope=\"col\" class=\"px-6 py-3 text-center\">Opening Amount</th>\n          <th scope=\"col\" class=\"px-6 py-3\"></th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr\n          class=\"border-b bg-white dark:border-gray-700 dark:bg-gray-900\"\n          v-for=\"(modeOfPayment, index) in invoiceData.modeOfPaymentList\"\n          :key=\"index\"\n        >\n          <th\n            scope=\"row\"\n            class=\"whitespace-nowrap px-6 py-4 font-medium text-gray-900 dark:text-white\"\n          >\n            {{ modeOfPayment.mode_of_payment }}\n          </th>\n          <td\n            class=\"px-6 py-4 text-center font-medium text-gray-900 dark:text-white\"\n          >\n            <input\n              type=\"number\"\n              id=\"amount\"\n              name=\"amount\"\n              v-model=\"modeOfPayment.opening_amount\"\n              class=\"border-none text-center\"\n              @input=\"posOpen.changePaidAmount(modeOfPayment.opening_amount)\"\n            />\n          </td>\n\n          <td class=\"px-6 py-4\">\n            <button\n              class=\"p-2 text-center\"\n              type=\"button\"\n              @click=\"this.posOpen.deleteRow(index)\"\n            >\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                width=\"25\"\n                height=\"25\"\n                fill=\"currentColor border\"\n                class=\"bi bi-trash\"\n                viewBox=\"0 0 16 16\"\n              >\n                <path\n                  d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z\"\n                ></path>\n                <path\n                  d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z\"\n                ></path>\n              </svg>\n            </button>\n          </td>\n        </tr>\n      </tbody>\n    </table>\n  </div>\n\n  <hr class=\"my-6 border-t border-gray-300\" />\n\n  <div\n    v-if=\"this.posOpen.showSumbitPosOpen\"\n    class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n  >\n    <div class=\"mt-5 flex items-center justify-center\">\n      <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n        <div class=\"flex items-center justify-between\">\n          <h3 class=\"text-xl text-gray-900 dark:text-white\">Confirm</h3>\n          <span class=\"sr-only\">Close</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"h-5 w-5 cursor-pointer\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            @click=\"this.posOpen.showSumbitPosOpen = false\"\n          >\n            <path\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n              stroke-width=\"2\"\n              d=\"M6 18L18 6M6 6l12 12\"\n            />\n          </svg>\n        </div>\n\n        <h3\n          class=\"mt-5 block text-left text-base text-gray-900 dark:text-white\"\n        >\n          Permanently Submit{{ this.posOpen.posOpenEntryName }}?\n        </h3>\n        <div class=\"flex justify-end\">\n          <button\n            @click=\"this.posOpen.showSumbitPosOpen = false\"\n            class=\"mr-3 mt-6 rounded border border-gray-300 bg-gray-50 px-3 py-2\"\n          >\n            No\n          </button>\n          <button\n            @click=\"this.posOpen.sumbitPosOpening()\"\n            class=\"mt-6 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n          >\n            Yes\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\nimport { posOpening } from \"@/stores/posOpening.js\";\n\nimport DatePicker from \"vue-datepicker-next\";\nimport \"vue-datepicker-next/index.css\";\nexport default {\n  name: \"posOpen\",\n  components: { DatePicker },\n  setup() {\n    const invoiceData = useInvoiceDataStore();\n    const posOpen = posOpening();\n    return { invoiceData, posOpen };\n  },\n  mounted() {\n    this.posOpen.setFormattedDate();\n  },\n};\n</script>\n<style>\n.my-custom-date-picker {\n  width: 100%;\n  color: black;\n}\n</style>\n"
  },
  {
    "path": "urypos/src/components/recentOrder.vue",
    "content": "<template>\n  <div class=\"mt-3 flex flex-col md:flex-row\">\n    <div\n      class=\"fixed inset-0 z-50 flex items-center justify-center bg-gray-300 bg-opacity-50 text-lg\"\n      v-if=\"this.invoiceData.isPrinting\"\n    >\n      Printing Invoice\n    </div>\n\n    <div\n      class=\"fixed inset-0 z-50 flex items-center justify-center bg-gray-300 bg-opacity-50 text-lg\"\n      v-if=\"this.recentOrders.isLoading\"\n    >\n      Payment Being Processing\n    </div>\n    <div\n      class=\"max-w-lg flex-1 rounded-lg border border-gray-200 bg-white p-4 shadow dark:border-gray-700 dark:bg-gray-800 sm:p-8\"\n    >\n      <div class=\"mb-4 flex items-center justify-between\">\n        <h5\n          class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\"\n        >\n          Recent Orders\n        </h5>\n      </div>\n      <div class=\"w-full\" @click=\"this.recentOrders.showOrder = false\">\n        <input\n          type=\"search\"\n          id=\"orderSeach\"\n          class=\"block w-full rounded-lg border-gray-300 bg-gray-50 p-2.5 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          placeholder=\"Search by Invoice Id or Customer Name or Mobile Number\"\n          v-model=\"this.recentOrders.searchOrder\"\n          @input=\"this.recentOrders.handleSearchInput\"\n        />\n        <select\n          id=\"status\"\n          class=\"mt-4 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          v-model=\"this.recentOrders.selectedStatus\"\n          @change=\"this.recentOrders.handleStatusChange\"\n        >\n          <option value=\"Draft\">Draft</option>\n          <option value=\"Unbilled\">Unbilled</option>\n          <option\n            value=\"Recently Paid\"\n            v-if=\"auth.viewAllStatus === 0 && invoiceData.paidLimit > 0\"\n          >\n            Recently Paid\n          </option>\n          <option value=\"Paid\" v-if=\"this.auth.viewAllStatus === 1\">\n            Paid\n          </option>\n          <option value=\"Consolidated\" v-if=\"this.auth.viewAllStatus === 1\">\n            Consolidated\n          </option>\n          <option value=\"Return\" v-if=\"this.auth.viewAllStatus === 1\">\n            Return\n          </option>\n        </select>\n      </div>\n      <div class=\"flow-root\">\n        <ul role=\"list\" class=\"divide-y divide-gray-200 dark:divide-gray-700\">\n          <li\n            class=\"mt-2 py-3 sm:py-4\"\n            :class=\"{\n              'bg-gray-200': this.recentOrders.setBackground === index,\n            }\"\n            v-for=\"(recentOrder, index) in this.recentOrders.filteredOrders\"\n            :key=\"recentOrder.name\"\n            @click=\"\n              this.recentOrders.viewRecentOrder(recentOrder);\n              this.recentOrders.setBackground = index;\n            \"\n          >\n          <div class=\"flex w-full\">\n              <div class=\"w-3/5\">\n                <p\n                  class=\"truncate text-base font-medium text-gray-900 dark:text-white\"\n                >\n                  {{ recentOrder.name }}\n                </p>\n                <p class=\"truncate text-sm text-gray-600 dark:text-gray-400\">\n                  {{ recentOrder.mobile_number }},{{ recentOrder.customer }}\n                </p>\n              </div>\n              <div class=\"w-2/5 flex\">\n                <p class=\"text-base font-medium text-gray-900 dark:text-white overflow-hidden text-ellipsis\">\n                  {{\n                    recentOrder.restaurant_table\n                      ? recentOrder.restaurant_table\n                      : recentOrder.order_type\n                  }}\n                </p>\n              </div>\n              <div class=\"w-1/5 text-right\">\n                <p\n                  class=\"truncate text-base font-medium text-gray-900 dark:text-white\"\n                  >\n                  {{ this.invoiceData.currency }}\n                  {{ recentOrder.grand_total }}\n                </p>\n                <p class=\"truncate text-sm text-gray-600 dark:text-gray-400\">\n                  {{\n                    this.recentOrders.getFormattedTime(\n                      recentOrder.posting_time\n                    )\n                  }}\n                </p>\n              </div>\n            </div>\n          </li>\n        </ul>\n      </div>\n      <div class=\"mt-4 flex justify-center\">\n        <button\n          :class=\"{ hidden: this.recentOrders.currentPage === 1 }\"\n          @click=\"this.recentOrders.previousPageClick()\"\n          class=\"mr-2 w-[80px] rounded-md border px-2 py-1\"\n        >\n          Previous\n        </button>\n        <button class=\"mr-2 rounded-md border px-2 py-1\">\n          {{ this.recentOrders.currentPage }}\n        </button>\n        <button\n          @click=\"this.recentOrders.nextPageClick()\"\n          v-if=\"this.recentOrders.next\"\n          class=\"w-[80px] rounded-md border px-2 py-1\"\n        >\n          Next\n        </button>\n      </div>\n    </div>\n    <div\n      class=\"mt-5 max-w-lg flex-1 rounded-lg border border-gray-200 bg-white p-4 shadow dark:border-gray-700 dark:bg-gray-800 sm:p-8 md:ml-10 md:mt-0\"\n      v-if=\"this.recentOrders.showOrder\"\n    >\n      <div class=\"flex items-center space-x-4\">\n        <div class=\"min-w-0 flex-1\">\n          <p\n            class=\"truncate text-xl font-semibold text-gray-900 dark:text-white\"\n          >\n            {{ this.recentOrders.selectedOrder.customer }}\n          </p>\n          <p\n            class=\"truncate text-xl font-semibold text-gray-900 dark:text-white\"\n          >\n            {{ this.recentOrders.selectedOrder.mobile_number }}\n          </p>\n          <p\n            class=\"mr-2 mt-2 truncate text-sm text-gray-500 dark:text-gray-400\"\n          >\n            {{ this.recentOrders.postingDate }}\n          </p>\n\n          <p\n            class=\"mr-2 mt-2 truncate text-sm text-gray-500 dark:text-gray-400\"\n            v-if=\"this.recentOrders.selectedOrder.waiter\"\n          >\n            Waiter : {{ this.recentOrders.selectedOrder.waiter }}\n          </p>\n        </div>\n        <div class=\"items-center space-x-4 text-right\">\n          <div class=\"min-w-0 flex-1\">\n            <p\n              class=\"mr-2 truncate text-xl font-semibold text-gray-900 dark:text-white\"\n            >\n              {{ this.invoiceData.currency }}\n              {{\n                this.recentOrders.selectedOrder.status === \"Draft\"\n                  ? \"0.00\"\n                  : this.recentOrders.selectedOrder.grand_total\n              }}\n            </p>\n            <p\n              class=\"mr-2 mt-2 truncate text-sm text-gray-500 dark:text-gray-400\"\n            >\n              {{ this.recentOrders.selectedOrder.name }}\n            </p>\n\n            <div class=\"ml-5 mt-2\">\n              <Badge\n                :type=\"\n                  this.recentOrders.getBadgeType(\n                    this.recentOrders.selectedOrder\n                  )\n                \"\n              >\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  width=\"16\"\n                  height=\"16\"\n                  fill=\"currentColor\"\n                  class=\"bi bi-dot\"\n                  viewBox=\"0 0 16 16\"\n                >\n                  <path d=\"M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z\" />\n                </svg>\n                <span class=\"text-xs\">\n                  {{ this.recentOrders.selectedOrder.status }}\n                </span>\n              </Badge>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div class=\"mb-2 mt-4\">\n        <p class=\"truncate text-lg font-semibold text-gray-900 dark:text-white\">\n          Items\n        </p>\n      </div>\n      <div class=\"w-full rounded bg-gray-50 p-2\">\n        <div\n          class=\"ml-2 mt-2\"\n          v-for=\"items in this.recentOrders.recentOrderListItems\"\n        >\n          <div class=\"flex items-center space-x-4\">\n            <div class=\"min-w-2 flex-1\">\n              <p class=\"truncate text-base text-gray-800 dark:text-white\">\n                {{ items.item_name }}\n              </p>\n            </div>\n            <div class=\"flex items-center space-x-4 text-right\">\n              <p class=\"text-base text-gray-800 dark:text-white\">\n                {{ items.qty }}\n              </p>\n            </div>\n            <div class=\"items-center space-x-4 text-right\">\n              <p class=\"mr-5 truncate text-base text-gray-800 dark:text-white\">\n                {{ this.invoiceData.currency }} {{ items.amount }}\n              </p>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div\n        class=\"mt-4 rounded-md border-2 border-dotted\"\n        :class=\"[\n          {\n            'border-gray-600': !recentOrders.showDiscount,\n            'border-green-500': recentOrders.showDiscount,\n            'border-red-500':recentOrders.totalAmount <= 0\n          },\n        ]\"\n        v-if=\"\n          invoiceData.enableDiscount == 1 &&\n          !recentOrders.showInput &&\n          this.recentOrders.selectedStatus === 'Draft'\n        \"\n      >\n        <div\n          @click=\"recentOrders.toggleDiscount\"\n          :class=\"{\n            'flex p-3': !recentOrders.showInput && !recentOrders.showDiscount,\n            'flex p-3 text-green-500': recentOrders.showDiscount,\n            'flex p-3 text-red-500':recentOrders.totalAmount <= 0\n          }\"\n        >\n        <svg\n            class=\"discount-icon\"\n            width=\"24\"\n            height=\"24\"\n            viewBox=\"0 0 24 24\"\n            stroke=\"currentColor\"\n            fill=\"none\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <path\n              d=\"M19 15.6213C19 15.2235 19.158 14.842 19.4393 14.5607L20.9393 13.0607C21.5251 12.4749 21.5251 11.5251 20.9393 10.9393L19.4393 9.43934C19.158 9.15804 19 8.7765 19 8.37868V6.5C19 5.67157 18.3284 5 17.5 5H15.6213C15.2235 5 14.842 4.84196 14.5607 4.56066L13.0607 3.06066C12.4749 2.47487 11.5251 2.47487 10.9393 3.06066L9.43934 4.56066C9.15804 4.84196 8.7765 5 8.37868 5H6.5C5.67157 5 5 5.67157 5 6.5V8.37868C5 8.7765 4.84196 9.15804 4.56066 9.43934L3.06066 10.9393C2.47487 11.5251 2.47487 12.4749 3.06066 13.0607L4.56066 14.5607C4.84196 14.842 5 15.2235 5 15.6213V17.5C5 18.3284 5.67157 19 6.5 19H8.37868C8.7765 19 9.15804 19.158 9.43934 19.4393L10.9393 20.9393C11.5251 21.5251 12.4749 21.5251 13.0607 20.9393L14.5607 19.4393C14.842 19.158 15.2235 19 15.6213 19H17.5C18.3284 19 19 18.3284 19 17.5V15.6213Z\"\n              stroke-miterlimit=\"10\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            ></path>\n            <path\n              d=\"M15 9L9 15\"\n              stroke-miterlimit=\"10\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            ></path>\n            <path\n              d=\"M10.5 9.5C10.5 10.0523 10.0523 10.5 9.5 10.5C8.94772 10.5 8.5 10.0523 8.5 9.5C8.5 8.94772 8.94772 8.5 9.5 8.5C10.0523 8.5 10.5 8.94772 10.5 9.5Z\"\n              fill=\"white\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            ></path>\n            <path\n              d=\"M15.5 14.5C15.5 15.0523 15.0523 15.5 14.5 15.5C13.9477 15.5 13.5 15.0523 13.5 14.5C13.5 13.9477 13.9477 13.5 14.5 13.5C15.0523 13.5 15.5 13.9477 15.5 14.5Z\"\n              fill=\"white\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            ></path>\n          </svg>\n\n          <template v-if=\"!recentOrders.showDiscount\">\n            <span>Add Discount</span>\n          </template>\n\n          <template v-else>\n            <span v-if=\"recentOrders.totalAmount > 0\">\n              Additional {{ recentOrders.percentage }}% discount Applied\n            </span>\n            <span v-else class=\"text-red-500\">\n              {{ recentOrders.percentage }}% cannot be Applied\n            </span>\n          </template>\n        </div>\n      </div>\n      <div class=\"relative mb-6 mt-6\" v-if=\"this.recentOrders.showInput\">\n        <input\n          type=\"number\"\n          class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500\"\n          placeholder=\"Enter Discount Percentage\"\n          v-model=\"this.recentOrders.percentage\"\n          @input=\"this.recentOrders.updatePercentage\"\n          @keyup.enter=\"this.recentOrders.applyDiscount\"\n          @keyup=\"this.recentOrders.resetTimer\"\n        />\n      </div>\n      <div class=\"mb-2 mt-5\">\n        <p class=\"truncate text-lg font-semibold text-gray-900 dark:text-white\">\n          Totals\n        </p>\n      </div>\n      <div class=\"w-full rounded bg-gray-50 p-2\">\n        <div class=\"ml-2 mt-2 flex items-center space-x-4\">\n          <div class=\"min-w-2 flex-1\">\n            <p class=\"truncate text-base text-gray-800 dark:text-white\">\n              Net Total\n            </p>\n          </div>\n\n          <div class=\"items-center space-x-4 text-right\">\n            <p class=\"mr-5 truncate text-base text-gray-800 dark:text-white\">\n              {{ this.invoiceData.currency }} {{ this.recentOrders.netTotal }}\n            </p>\n          </div>\n        </div>\n        <div class=\"ml-2\" v-for=\"tax in this.recentOrders.texDetails\">\n          <div class=\"mt-2 flex items-center space-x-4\">\n            <div class=\"min-w-2 flex-1\">\n              <p class=\"truncate text-base text-gray-800 dark:text-white\">\n                {{ tax.description }}\n              </p>\n            </div>\n\n            <div class=\"items-center space-x-4 text-right\">\n              <p class=\"mr-5 truncate text-base text-gray-800 dark:text-white\">\n                {{ this.invoiceData.currency }} {{ tax.rate }}\n              </p>\n            </div>\n          </div>\n        </div>\n        <div\n          class=\"ml-2 mt-2 flex items-center space-x-4\"\n          v-if=\"this.recentOrders.additionalPiscountPercentage\"\n        >\n          <div class=\"min-w-2 flex-1\">\n            <p\n              class=\"truncate text-base font-semibold text-gray-800 dark:text-white\"\n            >\n              Discount({{ this.recentOrders.additionalPiscountPercentage }})\n            </p>\n          </div>\n          <div class=\"items-center space-x-4 text-right\">\n            <p\n              class=\"mr-5 truncate text-base font-semibold text-gray-800 dark:text-white\"\n            >\n              {{ this.invoiceData.currency }}\n              {{ this.recentOrders.discountAmount }}\n            </p>\n          </div>\n        </div>\n        <div class=\"ml-2 mt-2 flex items-center space-x-4\">\n          <div class=\"min-w-2 flex-1\">\n            <p\n              class=\"truncate text-base font-semibold text-gray-800 dark:text-white\"\n            >\n              Grand Total\n            </p>\n          </div>\n          <div class=\"items-center space-x-4 text-right\">\n            <p\n              class=\"mr-5 truncate text-base font-semibold text-gray-800 dark:text-white\"\n            >\n              {{ this.invoiceData.currency }}\n              {{\n                this.recentOrders.totalAmount > 0\n                  ? this.recentOrders.totalAmount\n                  : this.recentOrders.grandTotal\n              }}\n            </p>\n          </div>\n        </div>\n      </div>\n      <div\n        class=\"mt-2 rounded px-4 py-2 text-center\"\n        v-if=\"\n          this.recentOrders.selectedStatus !== 'Draft' &&\n          recentOrders.selectedStatus !== 'Unbilled'\n        \"\n      >\n        <button\n          type=\"button\"\n          class=\"mb-2 mr-2 rounded-lg border border-gray-400 bg-white px-5 py-2.5 text-sm font-medium text-gray-800 focus:outline-none dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400\"\n          @click=\"this.invoiceData.printFunction()\"\n        >\n          Print Receipt\n        </button>\n      </div>\n      <div\n        class=\"mt-2 rounded px-4 py-2 text-center\"\n        v-if=\"\n          this.recentOrders.selectedStatus === 'Draft' ||\n          recentOrders.selectedStatus === 'Unbilled'\n        \"\n      >\n        <button\n          type=\"button\"\n          class=\"mb-2 mr-2 w-36 rounded-lg border bg-white px-5 py-2.5 text-sm font-medium focus:outline-none dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400\"\n          :class=\"{\n            'border-gray-200 text-gray-300':\n              this.recentOrders.orderType === 'Aggregators',\n            'border-gray-300 text-gray-700':\n              this.recentOrders.orderType !== 'Aggregators',\n          }\"\n          @click=\"\n            this.recentOrders.orderType !== 'Aggregators'\n              ? this.recentOrders.editOrder()\n              : ''\n          \"\n        >\n          Edit\n        </button>\n        <button\n          type=\"button\"\n          class=\"mb-2 mr-2 w-36 rounded-lg border border-gray-300 bg-white px-5 py-2.5 text-sm font-medium text-gray-700 focus:outline-none dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400\"\n          @click=\"this.invoiceData.printFunction()\"\n        >\n          Print Receipt\n        </button>\n      </div>\n      <div\n        class=\"mt-2 rounded px-4 py-2 text-center\"\n        v-if=\"\n          this.recentOrders.selectedStatus === 'Draft' ||\n          this.recentOrders.selectedStatus === 'Unbilled'\n        \"\n      >\n        <button\n          type=\"button\"\n          class=\"mb-2 mr-2 w-36 rounded-lg border border-gray-300 bg-white px-5 py-2.5 text-sm font-medium text-gray-700 focus:outline-none dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400\"\n          @click=\"this.recentOrders.billing()\"\n        >\n          Make Payment\n        </button>\n        <button\n          type=\"button\"\n          class=\"mb-2 mr-2 w-36 rounded-lg border bg-white px-5 py-2.5 text-sm font-medium focus:outline-none dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400\"\n          :class=\"{\n            'border-gray-200 text-gray-300':\n              this.recentOrders.invoicePrinted === 1 ||\n              this.recentOrders.selectedStatus === 'Unbilled',\n            'border-gray-300 text-gray-700': !(\n              this.recentOrders.invoicePrinted === 1 ||\n              this.recentOrders.selectedStatus === 'Unbilled'\n            ),\n          }\"\n          @click=\"\n            this.recentOrders.invoicePrinted === 0 &&\n            this.recentOrders.selectedStatus === 'Draft'\n              ? this.recentOrders.showCancelInvoiceModal()\n              : ''\n          \"\n        >\n          Cancel Order\n        </button>\n      </div>\n      <div\n        v-if=\"this.recentOrders.cancelInvoiceFlag === true\"\n        class=\"fixed inset-0 z-10 mt-20 overflow-y-auto bg-gray-100\"\n      >\n        <div class=\"mt-20 flex items-center justify-center\">\n          <div class=\"w-full rounded-lg bg-white p-6 shadow-lg md:max-w-md\">\n            <div class=\"flex justify-end\">\n              <span class=\"sr-only\">Close</span>\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"h-5 w-5\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n                @click=\"this.recentOrders.cancelInvoiceFlag = false\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  stroke-width=\"2\"\n                  d=\"M6 18L18 6M6 6l12 12\"\n                />\n              </svg>\n            </div>\n            <h2\n              class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n            >\n              Are you sure to cancel\n            </h2>\n            <div class=\"relative\">\n              <label\n                for=\"cancelReason\"\n                class=\"mt-6 block text-left text-gray-900 dark:text-white\"\n              >\n                Reason\n              </label>\n              <input\n                type=\"text\"\n                id=\"cancelReason\"\n                class=\"mt-4 w-full appearance-none rounded border p-2 leading-tight text-gray-900 shadow focus:outline-none\"\n                v-model=\"this.recentOrders.cancelReason\"\n              />\n            </div>\n            <div class=\"flex justify-end\">\n              <button\n                @click=\"this.recentOrders.cancelInvoiceFlag = false\"\n                class=\"mr-3 mt-6 rounded border border-gray-300 bg-gray-50 px-3 py-2\"\n              >\n                No\n              </button>\n              <button\n                @click=\"handleConfirmCancellation()\"\n                class=\"mt-6 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n              >\n                Yes\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div\n        v-if=\"this.recentOrders.showPayment\"\n        class=\"fixed inset-0 z-10 mt-14 overflow-y-auto bg-gray-100\"\n      >\n        <div class=\"mt-10 flex items-center justify-center\">\n          <div class=\"h-82 w-full rounded-lg bg-white p-6 shadow-lg md:w-3/5\">\n            <div class=\"flex justify-end\">\n              <span class=\"sr-only\">Close</span>\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"h-5 w-5\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n                @click=\"this.recentOrders.showPayment = false\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  stroke-width=\"2\"\n                  d=\"M6 18L18 6M6 6l12 12\"\n                />\n              </svg>\n            </div>\n            <h2\n              class=\"mt-1 block text-left text-xl font-medium text-gray-900 dark:text-white\"\n            >\n              Select Mode Of Payment\n            </h2>\n            <div class=\"mt-8 flex items-center justify-center\">\n              <div class=\"w-full max-w-full overflow-x-auto\">\n                <div class=\"flex flex-nowrap\">\n                  <div\n                    v-for=\"(\n                      modeOfPayment, index\n                    ) in recentOrders.modeOfPaymentList\"\n                    :key=\"index\"\n                    class=\"mr-4 w-64 flex-shrink-0 rounded-lg border border-gray-200 bg-white p-4 shadow dark:border-gray-700 dark:bg-gray-800\"\n                  >\n                    <label\n                      :for=\"'modeofPayments-' + index\"\n                      class=\"block text-left text-lg dark:text-white\"\n                    >\n                      {{ modeOfPayment.mode_of_payment }}\n                    </label>\n                    <input\n                      :id=\"'modeofPayments-' + index\"\n                      type=\"number\"\n                      name=\"modeofPayments\"\n                      class=\"block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-500 dark:bg-gray-600 dark:text-white dark:placeholder-gray-400\"\n                      required\n                      v-model.number=\"modeOfPayment.value\"\n                      @click=\"recentOrders.calculatePaidAmount(modeOfPayment)\"\n                      @input=\"\n                        recentOrders.changePaidAmount(\n                          modeOfPayment.mode_of_payment,\n                          $event.target.value\n                        )\n                      \"\n                    />\n                  </div>\n                </div>\n              </div>\n            </div>\n            <div v-if=\"recentOrders.changeAmount > 0\" class=\"mt-4 p-4 bg-gray-50 rounded-lg\">\n              <div class=\"flex justify-between items-center mt-2 text-green-600\">\n                <span class=\"text-lg font-medium\">Change Amount:</span>\n                <span class=\"text-lg\">₹ {{ recentOrders.changeAmount.toFixed(2) }}</span>\n              </div>\n            </div>\n            <div class=\"flex justify-end\">\n              <button\n                @click=\"\n                  this.recentOrders.showPayment = false;\n                  this.recentOrders.makePayment();\n                \"\n                class=\"mt-10 rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600\"\n              >\n                Submit\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { usetoggleRecentOrder } from \"@/stores/recentOrder.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport { Badge } from \"flowbite-vue\";\nimport { useNotifications } from \"@/stores/Notification.js\";\nexport default {\n  name: \"RecentOrder\",\n  components: {\n    Badge,\n  },\n  methods: {\n    handleConfirmCancellation() {\n      if (!this.recentOrders.cancelReason || this.recentOrders.cancelReason.trim() === '') {\n        this.notification.createNotification('Please enter a reason for cancellation');\n        return;\n      }\n      this.recentOrders.cancelInvoice();\n      this.recentOrders.cancelInvoiceFlag = false;\n    },\n  },\n  setup() {\n    const recentOrders = usetoggleRecentOrder();\n    const invoiceData = useInvoiceDataStore();\n    const auth = useAuthStore();\n    const notification = useNotifications();\n    return { recentOrders, invoiceData, auth, notification };\n  },\n  mounted() {\n    this.recentOrders.handleStatusChange();\n  },\n};\n</script>\n<style>\n.bg-gray-100 {\n  background-color: rgba(0, 0, 0, 0.2);\n}\n</style>\n"
  },
  {
    "path": "urypos/src/components/takeAwayTable.vue",
    "content": "<template>\n  <div class=\"flow-root\">\n    <div\n      class=\"fixed inset-0 z-50 flex items-center justify-center bg-gray-300 bg-opacity-50 text-lg\"\n      v-if=\"this.invoiceData.isPrinting\"\n    >\n      Printing Invoice\n    </div>\n    <div class=\"grid grid-cols-2 gap-4 md:grid-cols-4 lg:grid-cols-5\">\n      <div\n        w-full\n        class=\"w-full max-w-sm rounded border border-gray-200 bg-white shadow dark:border-gray-700 dark:bg-gray-800\"\n        v-for=\"table in this.table.takeAway\"\n        :key=\"table.name\"\n      >\n        <div class=\"flex justify-between\">\n          <div class=\"flex justify-start px-2 pt-2\">\n            <span\n              class=\"me-2 rounded px-2.5 py-0.5 text-sm font-medium\"\n              :class=\"{\n                'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300':\n                  this.table.getBadgeType(table) === 'default',\n                'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300':\n                  this.table.getBadgeType(table) === 'red',\n                'bg-yellow-100 text-yellow-800':\n                  this.table.getBadgeType(table) === 'yellow',\n                'bg-green-100 text-green-800':\n                  this.table.getBadgeType(table) === 'green',\n              }\"\n            >\n              {{ this.table.getBadgeText(table) }}\n            </span>\n          </div>\n          <div class=\"relative\" v-if=\"table.occupied === 1\">\n            <button\n              class=\"inline-block rounded p-1.5 text-sm text-gray-500 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-700\"\n              type=\"button\"\n              @click=\"this.table.toggleDropdown(table.name)\"\n            >\n              <svg\n                class=\"h-6 w-6\"\n                aria-hidden=\"true\"\n                fill=\"currentColor\"\n                viewBox=\"0 0 20 20\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n              >\n                <path\n                  d=\"M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z\"\n                ></path>\n              </svg>\n            </button>\n            <div\n              class=\"absolute right-0 z-10 w-36 divide-y divide-gray-100 rounded bg-white shadow dark:bg-gray-700\"\n              v-show=\"this.table.activeDropdown === table.name\"\n            >\n              <ul class=\"py-2\">\n                <li>\n                  <a\n                    href=\"#\"\n                    class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white\"\n                    @click=\"this.table.showModal = true\"\n                    >Table Transfer</a\n                  >\n                </li>\n                <li v-if=\"this.auth.hasAccess\">\n                  <a\n                    href=\"#\"\n                    class=\"block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white\"\n                    @click=\"this.table.showModalCaptainTransfer = true\"\n                    >Captain Transfer</a\n                  >\n                </li>\n              </ul>\n            </div>\n          </div>\n        </div>\n        <div class=\"flex flex-col pb-4\">\n          <div\n            class=\"mt-1 text-center\"\n            @click=\"\n              table.occupied === 1 && !this.auth.restrictTableOrder\n                ? this.table.routeToMenu(table)\n                : ''\n            \"\n          >\n            <h5\n              class=\"mt-2 text-xl font-medium text-gray-900 dark:text-white\"\n              :class=\"{ 'mt-3': table.occupied === 0 }\"\n            >\n              {{ table.name }}\n            </h5>\n            <span class=\"text-sm text-gray-500 dark:text-gray-400\">{{\n              table.occupied === 1 ? this.table.getTimeDifference(table) : \"\"\n            }}</span>\n          </div>\n          <div class=\"mt-8 text-center\" v-if=\"table.occupied != 1\">\n            <button\n              type=\"button\"\n              class=\"inline-flex items-center rounded px-2 py-2.5 text-center text-sm font-medium text-white hover:bg-[#2557D6]/90 focus:outline-none focus:ring-4 focus:ring-[#2557D6]/50 dark:focus:ring-[#2557D6]/50\"\n              :class=\"[\n                {\n                  'bg-blue-700': !this.auth.restrictTableOrder,\n                  'pointer-events-none bg-blue-400':\n                    this.auth.restrictTableOrder,\n                },\n              ]\"\n              @click=\"\n                !this.auth.restrictTableOrder &&\n                  this.table.addToSelectedTables(table)\n              \"\n            >\n              Open Table\n              <svg\n                class=\"ml-2 h-6 w-6 dark:text-white\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                viewBox=\"0 0 24 24\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n              >\n                <path\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  stroke-width=\"2\"\n                  d=\"M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z\"\n                ></path>\n              </svg>\n            </button>\n          </div>\n          <div class=\"mt-2 flex justify-center\" v-if=\"table.occupied === 1\">\n            <button\n              type=\"button\"\n              class=\"mb-2 me-2 inline-flex items-center rounded bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-[#2557D6]/90 focus:outline-none focus:ring-4 focus:ring-[#2557D6]/50 dark:focus:ring-[#2557D6]/50\"\n              @click=\"this.invoiceData.billing(table)\"\n            >\n              <svg\n                class=\"svg-icon mr-2\"\n                viewBox=\"0 0 24 24\"\n                width=\"18\"\n                height=\"18\"\n                fill=\"white\"\n              >\n                <path\n                  d=\"M6 19H3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h3V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v4h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1h-3v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2zm0-2v-1a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1h2V9H4v8h2zM8 4v3h8V4H8zm0 13v3h8v-3H8zm-3-7h3v2H5v-2z\"\n                />\n              </svg>\n              Bill\n            </button>\n            <div\n              class=\"relative inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full border hover:bg-blue-700 hover:text-white focus:outline-none focus:ring-4 focus:ring-blue-300 dark:border-blue-500 dark:text-blue-500 dark:hover:bg-blue-500 dark:hover:text-white dark:focus:ring-blue-800\"\n              :class=\"[\n                {\n                  'border-blue-700 text-blue-700':\n                    !this.auth.restrictTableOrder,\n                  'pointer-events-none border-blue-400 text-blue-400':\n                    this.auth.restrictTableOrder,\n                },\n              ]\"\n              @click=\"\n                !this.auth.restrictTableOrder && this.table.routeToCart(table)\n              \"\n            >\n              <svg\n                aria-hidden=\"true\"\n                class=\"h-10 w-6\"\n                fill=\"currentColor\"\n                viewBox=\"0 0 20 20\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n              >\n                <path d=\"M10 12a2 2 0 100-4 2 2 0 000 4z\"></path>\n                <path\n                  fill-rule=\"evenodd\"\n                  d=\"M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z\"\n                  clip-rule=\"evenodd\"\n                ></path>\n              </svg>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <div\n    v-if=\"this.table.takeAway.length === 0\"\n    class=\"inset-0 mt-72 flex items-center justify-center\"\n  >\n    <div class=\"text-center\">\n      Tables not found. Please set takeaway tables for the room\n      <span class=\"font-medium\">{{ this.table.selectedRoom }}.</span>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { useTableStore } from \"@/stores/Table.js\";\nimport { useInvoiceDataStore } from \"@/stores/invoiceData.js\";\nimport { useAuthStore } from \"@/stores/Auth.js\";\n\nexport default {\n  name: \"takeAwayTable\",\n\n  setup() {\n    const table = useTableStore();\n    const invoiceData = useInvoiceDataStore();\n    const auth = useAuthStore();\n    return { table, invoiceData, auth };\n  },\n};\n</script>"
  },
  {
    "path": "urypos/src/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\t"
  },
  {
    "path": "urypos/src/main.js",
    "content": "import './index.css';\nimport { createApp, reactive } from \"vue\";\nimport App from \"./App.vue\";\n\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport router from './router';\nimport { createPinia } from 'pinia'\nimport NotificationModal from './components/NotificationModal.vue';\n\n\n\nconst pinia = createPinia()\nconst app = createApp(App);\n\napp.use(router);\napp.use(pinia)\n\n\nrouter.beforeEach((to, from, next) => {\n\tconst auth = useAuthStore();\n\tconst isAuthenticated = auth.userAuth\n\n\tif (to.name !== 'Login' && !isAuthenticated) {\n\t\tnext({ name: 'Login' });\n\t} else if (to.name === 'Login' && isAuthenticated) {\n\t\tnext({ name: 'Table' });\n\t} else {\n\t\tnext();\n\t}\n});\n\napp.mount(\"#app\");\napp.component('NotificationModal', NotificationModal);\n\n"
  },
  {
    "path": "urypos/src/router/auth.js",
    "content": "export default [\n    {\n\t\tpath: '/login',\n\t\tname: 'Login',\n\t\tcomponent: () =>\n\t\t\timport(/* webpackChunkName: \"login\" */ '../components/Login.vue'),\n\t\tmeta: {\n\t\t\tisLoginPage: true\n\t\t},\n\t\tprops: true\n\t}\n]\n"
  },
  {
    "path": "urypos/src/router/index.js",
    "content": "import { createRouter, createWebHistory } from \"vue-router\";\nimport authRoutes from './auth';\nimport { useAuthStore } from \"@/stores/Auth.js\";\nimport Table from \"../components/Table.vue\";\nimport Customer from \"../components/Customer.vue\";\nimport Menu from \"../components/Menu.vue\";\nimport Cart from \"../components/Cart.vue\";\nimport recentOrder from \"../components/recentOrder.vue\";\nimport Login from \"../components/Login.vue\";\nimport posOpen from \"../components/posOpening.vue\";\nimport posClose from \"../components/posClosing.vue\";\n\n\n\nconst routes = [\n  {\n    path: \"/\",\n    name: \"Table\",\n    component: Table,\n  },\n  {\n    path: \"/Table\",\n    name: \"Table\",\n    component: Table,\n  },\n  {\n    path: \"/Customer\",\n    name: \"Customer\",\n    component: Customer,\n  },\n  {\n    path: \"/Menu\",\n    name: \"Menu\",\n    component: Menu,\n  },\n  {\n    path: \"/Cart\",\n    name: \"Cart\",\n    component: Cart,\n  },\n  {\n    path: \"/recentOrder\",\n    name: \"recentOrder\",\n    component: recentOrder,\n  },\n  {\n    path: \"/PosOpen\",\n    name: \"posOpen\",\n    component: posOpen,\n  },\n  {\n    path: \"/PosClose\",\n    name: \"posClose\",\n    component: posClose\n    ,\n  },  \n  ...authRoutes,\n];\n\nexport const router = createRouter({\n  history: createWebHistory('/urypos/'),\n  routes,\n});\n\n\n\nexport default router;\n\n\n\n"
  },
  {
    "path": "urypos/src/stores/Alert.js",
    "content": "import { defineStore } from \"pinia\";\n\nexport const useAlert = defineStore(\"alert\", {\n  state: () => ({\n    okButtonClicked: false,\n  }),\n  actions: {\n    createAlert(title, message, buttonText) {\n      return new Promise((resolve) => {\n        // Create a backdrop for the modal\n        const backdrop = document.createElement(\"div\");\n        backdrop.classList.add(\n          \"fixed\",\n          \"inset-0\",\n          \"z-50\",\n          \"bg-black\",\n          \"opacity-50\",\n          \"backdrop-blur-md\"\n        );\n        document.body.appendChild(backdrop);\n\n        // Create the modal container\n        const modal = document.createElement(\"div\");\n        modal.classList.add(\n          \"fixed\",\n          \"top-10\",\n          \"z-50\",\n          \"lg:left-1/2\",\n          \"transform\",\n          \"lg:-translate-x-1/2\",\n          \"bg-white\",\n          \"p-6\",\n          \"rounded-lg\",\n          \"shadow-lg\",\n          \"w-100\"\n        );\n        const mediaQuery = window.matchMedia(\"(max-width: 767px)\");\n        if (mediaQuery.matches) {\n          modal.classList.remove(\"lg:left-1/2\", \"lg:-translate-x-1/2\");\n          modal.classList.add(\"left-0\", \"right-0\");\n        }\n        document.body.appendChild(modal);\n\n        // Create the modal content\n        const modalContent = document.createElement(\"div\");\n        modalContent.innerHTML = `\n          <h2 class=\"text-base font-semibold mb-4\">${title}</h2>\n          <hr class=\"my-6 border-t border-gray-300\" />\n\n          <p class=\"mb-4 text-justify text-sm\">${message}</p>\n          <button class=\"bg-blue-700 md:ml-96 ml-64 text-white px-4 py-2 rounded-md\">${buttonText}</button>\n        `;\n        modal.appendChild(modalContent);\n\n        // Close the modal and remove the backdrop when the button is clicked\n        const closeButton = modalContent.querySelector(\"button\");\n        closeButton.addEventListener(\"click\", () => {\n          modal.remove();\n          backdrop.remove();\n          resolve();\n          this.okButtonClicked = true;\n        });\n      });\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/Auth.js",
    "content": "import { defineStore } from \"pinia\";\nimport { useTableStore } from \"./Table.js\";\nimport { useMenuStore } from \"./Menu.js\";\nimport { useInvoiceDataStore } from \"./invoiceData.js\";\nimport frappe from \"./frappeSdk.js\";\n\nimport axios from \"axios\";\nimport { useAlert } from \"./Alert.js\";\nimport router from \"../router\";\nimport { disconnectQzPrinter } from \"./utils/PrintWithQz\";\n\naxios.defaults.baseURL = frappe.url;\n\nexport const useAuthStore = defineStore(\"auth\", {\n  state: () => ({\n    userId: \"\",\n    userName: \"\",\n    sessionUser: \"\",\n    currentPassword: \"\",\n    userRole: [],\n    isPosOpen: true,\n    hasAccess: false,\n    showPassword: false,\n    activeDropdown: false,\n    cashierisPosOpen: false,\n    cashier: null,\n    viewItemImage: null,\n    viewAllStatus: null,\n    restrictTableOrder: null,\n    removeTableOrderItem: null,\n    db: frappe.db(),\n    call: frappe.call(),\n    auth: frappe.auth(),\n    alert: useAlert(),\n    menu: useMenuStore(),\n    table: useTableStore(),\n    invoiceData: useInvoiceDataStore(),\n    userAuth: localStorage.getItem(\"userAuth\"),\n  }),\n  getters: {\n    isAuthenticated(state) {\n      state.isAuthenticated;\n    },\n    passwordFieldType() {\n      return this.showPassword ? \"text\" : \"password\";\n    },\n  },\n  actions: {\n    //Login\n    async login() {\n      try {\n        await this.auth.loginWithUsernamePassword({\n          username: this.userId,\n          password: this.currentPassword,\n          device: \"mobile\",\n        });\n        this.userAuth = true;\n        localStorage.setItem(\"userAuth\", \"true\");\n        await this.fetchUserDetails();\n      } catch (error) {\n        this.alert.createAlert(\"Message\", error.message, \"OK\");\n      }\n    },\n    checkAuthState() {\n      if (this.userAuth && localStorage.getItem(\"userAuth\")) {\n        this.userAuth = true;\n      }\n    },\n    getLoginAvatar() {\n      const atIndex = this.sessionUser.indexOf(\"@\");\n      if (atIndex > -1) {\n        this.userName = this.sessionUser.substring(0, atIndex);\n      } else {\n        this.userName = \"\";\n      }\n      return this.userName;\n    },\n    fetchUserDetails() {\n      const user = localStorage.getItem(\"userAuth\");\n      // if (user !== \"true\") {\n      //   this.userAuth = false;\n      //   localStorage.removeItem(\"userAuth\");\n      //   router.push(\"/login\");\n      //   return\n      // }\n      this.auth\n        .getLoggedInUser()\n        .then((user) => {\n          this.sessionUser = user;\n          if (!this.sessionUser) {\n            this.userAuth = false;\n            localStorage.removeItem(\"userAuth\");\n          } else {\n            this.userAuth = true;\n            this.invoiceData.fetchInvoiceDetails().then(() => {\n              router.push(\"/Table\");\n              this.table.fetchRoom();\n              this.fetchUserRole();\n            });\n          }\n        })\n        .catch((error) => {\n          this.userAuth = false;\n          localStorage.removeItem(\"userAuth\", \"true\");\n          router.push(\"/login\");\n        });\n    },\n    fetchUserRole() {\n      //Fetching role based on logged user\n      this.db\n        .getDoc(\"User\", this.sessionUser)\n        .then((doc) => {\n          this.userRole = doc.roles.map((item) => item.role);\n          const getPosProfile = {\n            doctype: \"POS Profile\",\n            name: this.invoiceData.posProfile,\n          };\n          this.call\n            .get(\"frappe.client.get\", getPosProfile)\n            .then((result) => {\n              var billingRoles = result.message.role_allowed_for_billing.map(\n                (role) => role.role\n              );\n              this.cashier = billingRoles.some((role) =>\n                this.userRole.includes(role)\n              );\n              if (this.cashier) {\n                this.menu.pickOrderType();\n                // this.menu.fetchItems();\n              }\n              this.isPosOpenChecking();\n              this.isPosCloseCheck();\n              var transferRoles = result.message.transfer_role_permissions.map(\n                (role) => role.role\n              );\n              this.hasAccess = transferRoles.some((role) =>\n                this.userRole.includes(role)\n              );\n              var restrictOrder =\n                result.message.role_restricted_for_table_order.map(\n                  (role) => role.role\n                );\n              this.restrictTableOrder = restrictOrder.some((role) =>\n                this.userRole.includes(role)\n              );\n              this.viewAllStatus = result.message.view_all_status;\n              this.removeTableOrderItem = result.message.remove_items;\n              this.viewItemImage = result.message.show_image;\n            })\n            .catch((error) => console.error(error));\n        })\n        .catch((error) => {\n          console.error(error);\n        });\n    },\n    routeToHome() {\n      var currentDomain = window.location.protocol + \"//\" + window.location.hostname;\n      window.location.href = currentDomain + \"/app/\";\n    },\n\n    isPosOpenChecking() {\n      if (this.invoiceData.multipleCashier) {\n        this.call\n          .get(\"ury.ury.doctype.ury_order.ury_order.pos_opening_check\")\n          .then((result) => {\n            var currentDomain = window.location.origin;\n            if (!result.message.opening_exists) {\n              this.alert.createAlert(\"Message\", \"POS Opening Entry is not created\", \"OK\").then(() => {\n                window.location.href = currentDomain + \"/app/\";\n              });\n            }\n\n          })\n          .catch((error) => {\n            var currentDomain = window.location.origin;\n            const serverMessages = JSON.parse(error._server_messages);\n            const innerMessageString = serverMessages[0];\n            const innerMessage = JSON.parse(innerMessageString);\n            const message = innerMessage.message;\n            this.alert.createAlert(\"Message\", message, \"OK\").then(() => {\n              window.location.href = currentDomain + \"/app/\";\n            });\n\n          });\n      } \n      else {\n        this.call\n          .get(\"ury.ury_pos.api.posOpening\")\n          .then((result) => {\n            const serverMessages = JSON.parse(result._server_messages);\n            const innerMessageString = serverMessages[0];\n            const innerMessage = JSON.parse(innerMessageString);\n            const message = innerMessage.message;\n            // if (this.cashier) {\n            //   this.alert.createAlert(\"Message\", message, \"OK\").then(() => {\n            //     router.push(\"/posOpen\");\n            //   });\n            // } else {\n            var currentDomain = window.location.origin;\n            this.alert.createAlert(\"Message\", message, \"OK\").then(() => {\n              window.location.href = currentDomain + \"/app/\";\n            });\n            // }\n          })\n          .catch((error) => {\n            // console.error(error)\n          });\n      }\n    },\n    isPosCloseCheck() {\n      const getPosProfile = {\n        pos_profile: this.invoiceData.posProfile,\n      };\n      this.call\n        .get(\"ury.ury_pos.api.validate_pos_close\", getPosProfile)\n        .then((result) => {\n          if (result.message === \"Failed\") {\n            var currentDomain = window.location.origin;\n            this.alert\n              .createAlert(\"Message\", \"Please close previous POS Entry\", \"OK\")\n              .then(() => {\n                window.location.href = currentDomain + \"/app/\";\n              });\n          }\n        })\n        .catch((error) => {\n          console.error(error);\n        });\n    },\n    toggleDropdown() {\n      if (this.activeDropdown) {\n        this.hideDropdown();\n      } else {\n        this.activeDropdown = true;\n      }\n    },\n    hideDropdown() {\n      this.activeDropdown = false;\n    },\n\n    logOut() {\n      this.auth\n        .logout()\n        .then(() => {\n          router.push(\"/login\").then(() => {\n            window.location.reload();\n          });\n          localStorage.removeItem(\"userAuth\", \"true\");\n          disconnectQzPrinter();\n        })\n        .catch((error) => console.error(error));\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/Customer.js",
    "content": "import { defineStore } from \"pinia\";\nimport { useTableStore } from \"./Table.js\";\nimport { useNotifications } from \"./Notification.js\";\nimport { usetoggleRecentOrder } from \"./recentOrder.js\";\nimport { useMenuStore } from \"./Menu.js\";\nimport frappe from \"./frappeSdk.js\";\nimport { useAlert } from \"./Alert.js\";\nexport const useCustomerStore = defineStore(\"customers\", {\n  state: () => ({\n    customer: [],\n    notification: useNotifications(),\n    search: \"\",\n    alert: useAlert(),\n    showCustomers: false,\n    showOrderType: false,\n    showEditOrderType: false,\n    newOrderType: null,\n    numberOfPax: \"\",\n    menu: useMenuStore(),\n    recentOrders: usetoggleRecentOrder(),\n    selectedCustomerName: \"\",\n    customerFavouriteItems: [],\n    showModalNewCustomer: false,\n    newCustomerMobileNo: \"\",\n    newCustomer: \"\",\n    orderType: [],\n    table: useTableStore(),\n    showCustomersGroup: false,\n    showCustomersTerritory: false,\n    showAddNewCustomer: true,\n    customerTerritoryList: [],\n    customerTerritory: null,\n    customerGroupList: [],\n    customerGroup: null,\n    call: frappe.call(),\n    db: frappe.db(),\n    timer: null,\n  }),\n  getters: {\n    isFlagSet() {\n      return this.customer.length === 0;\n    },\n  },\n  actions: {\n    async pickCustomer() {\n      if (!this.search.trim()) {\n        this.customer = [];\n        return;\n      }\n\n      const getscramblePattern = (text) => {\n        return `%${text.split(\"\").join(\"%\")}%`;\n      };\n\n      const pattern = getscramblePattern(this.search);\n\n      const searchParams = {\n        fields: [\"name\", \"customer_name\", \"mobile_number\"],\n        orFilters: [\n          [\"customer_name\", \"like\", pattern],\n          [\"mobile_number\", \"like\", pattern],\n          [\"name\", \"like\", pattern],\n        ],\n        limit: 5,\n        limit_start: 0,\n      };\n\n      this.db\n        .getDocList(\"Customer\", searchParams)\n        .then((docs) => {\n          this.customer = docs.map((doc) => ({\n            ...doc,\n            content: `Customer Name : ${doc.customer_name ?? \"\"} | Mobile Number : ${doc.mobile_number ?? \"\"\n              }`,\n          }));\n        })\n        .catch((error) => console.error(error));\n    },\n    handleSearchInput(event) {\n      this.search = event.target.value;\n      clearTimeout(this.timer);\n      this.timer = setTimeout(() => {\n        this.pickCustomer();\n      }, 500);\n    },\n    pickCustomerGroup() {\n      this.db\n        .getDocList(\"Customer Group\")\n        .then((docs) => {\n          this.customerGroupList = docs;\n        })\n        .catch((error) => console.error(error));\n    },\n    selectCustomerGroup(group) {\n      this.customerGroup = group.name;\n      this.showCustomersGroup = false;\n    },\n    pickCustomerTerritory() {\n      this.db\n        .getDocList(\"Territory\")\n        .then((docs) => {\n          this.customerTerritoryList = docs;\n        })\n        .catch((error) => console.error(error));\n    },\n    selectCustomerTerritory(group) {\n      this.customerTerritory = group.name;\n      this.showCustomersTerritory = false;\n    },\n    newCustomerData(name) {\n      this.showModalNewCustomer = true;\n      if (!isNaN(parseFloat(name)) && isFinite(name)) {\n        this.newCustomerMobileNo = name;\n      } else if (typeof name === \"string\") {\n        this.newCustomer = name;\n      } else {\n        this.alert.createAlert(\"Message\", \"Invalid Customer\", \"OK\");\n      }\n    },\n    editOrderType(orderType) {\n      this.showEditOrderType = true;\n      if (orderType == \"Take Away\") {\n        this.newOrderType = \"Delivery\";\n      } else if (orderType == \"Delivery\") {\n        this.newOrderType = \"Take Away\";\n      } else {\n        return;\n      }\n    },\n    selecetOrderType(order_type) {\n      this.showEditOrderType = false;\n      this.menu.selectedOrderType = order_type;\n      this.recentOrders.pastOrderType = order_type;\n    },\n\n    addNewCustomer: async function () {\n      if (!this.newCustomer || !this.newCustomerMobileNo) {\n        let missingFields = [];\n        if (!this.newCustomer) {\n          missingFields.push(\"Customer Name\");\n        }\n        if (!this.newCustomerMobileNo) {\n          missingFields.push(\"Mobile Number\");\n        }\n        if (!this.customerGroup) {\n          missingFields.push(\"Customer Group\");\n        }\n        if (!this.customerTerritory) {\n          missingFields.push(\"Territory\");\n        }\n        const missingFieldsMessage =\n          \"Following fields have missing values: \" + missingFields.join(\", \");\n        this.alert.createAlert(\"Message\", missingFieldsMessage, \"OK\");\n      } else {\n        this.showAddNewCustomer = false;\n        const db = frappe.db();\n        db.createDoc(\"Customer\", {\n          customer_name: this.newCustomer,\n          mobile_number: this.newCustomerMobileNo.toString(),\n          customer_group: this.customerGroup,\n          territory: this.customerTerritory,\n        })\n          .then((doc) => {\n            this.search = doc.name;\n            this.notification.createNotification(\"New Customer Created\");\n            this.showModalNewCustomer = false;\n          })\n          .catch((error) => {\n            const serverMessages = JSON.parse(error._server_messages);\n            const messageObject = JSON.parse(serverMessages[0]);\n            const message = messageObject.message;\n            this.alert.createAlert(\"Message\", message, \"OK\");\n          });\n      }\n    },\n    extractName(content) {\n      if (content) {\n        const mobileStartIndex = content.indexOf(\"Mobile Number :\");\n        if (mobileStartIndex !== -1) {\n          // Check for new delimiter '|' or end of string\n          let mobileEndIndex = content.indexOf(\"|\", mobileStartIndex);\n          if (mobileEndIndex === -1) {\n            // If no | found, check for old delimiter ||| just in case, but mostly it might be end of string\n            mobileEndIndex = content.indexOf(\"|||\", mobileStartIndex);\n          }\n\n          // If still -1, it means it might be at the end of the string\n          if (mobileEndIndex === -1) {\n            mobileEndIndex = content.length;\n          }\n\n          if (mobileEndIndex !== -1) {\n            const mobileNumber = content\n              .substring(mobileStartIndex + \"Mobile Number :\".length, mobileEndIndex)\n              .trim();\n\n            return mobileNumber;\n          }\n        }\n      }\n      return \"\";\n    },\n    searchCustomer() {\n      if (this.menu.selectedAggregator) {\n        this.showCustomers = false;\n        this.showAddNewCustomer = false;\n      } else {\n        this.showCustomers = true;\n        this.showAddNewCustomer = true;\n      }\n    },\n    async selectCustomer(customer) {\n      this.search = customer.name;\n\n      // Use direct property if available (new method), fallback to parsing (old method/compatibility)\n      if (customer.mobile_number) {\n        this.newCustomerMobileNo = customer.mobile_number;\n      } else {\n        const content = customer.content;\n        const mobileNumber = this.extractName(content);\n        if (mobileNumber) {\n          this.newCustomerMobileNo = mobileNumber;\n        }\n      }\n\n      this.showCustomers = false;\n      this.fectchCustomerFavouriteItem();\n    },\n    validateInput(event) {\n      let value = event.target.value;\n      if (value < 1) {\n        this.numberOfPax = \"\";\n        return;\n      }\n\n      if (value.toString().length > 3) {\n        this.numberOfPax = value.toString().slice(0, 3);\n      } else {\n        this.numberOfPax = value;\n      }\n    },\n    async fectchCustomerFavouriteItem() {\n      if (this.table.previousOrderdCustomer) {\n        this.selectedCustomerName = this.table.previousOrderdCustomer;\n      } else {\n        this.selectedCustomerName = this.search;\n      }\n\n      const searchParams = {\n        customer_name: this.selectedCustomerName,\n      };\n\n      this.call\n        .get(\n          \"ury.ury.doctype.ury_order.ury_order.customer_favourite_item\",\n          searchParams\n        )\n        .then((result) => {\n          this.customerFavouriteItems = [];\n          result.message.forEach((item) => {\n            this.customerFavouriteItems.push(item);\n          });\n        })\n        .catch((error) => console.error(error));\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/Menu.js",
    "content": "import { defineStore } from \"pinia\";\nimport { useInvoiceDataStore } from \"./invoiceData.js\";\nimport { useTableStore } from \"./Table.js\";\nimport { useNotifications } from \"./Notification.js\";\nimport { useCustomerStore } from \"./Customer.js\";\nimport { useAuthStore } from \"./Auth.js\";\nimport frappe from \"./frappeSdk.js\";\nimport { usetoggleRecentOrder } from \"./recentOrder.js\";\nimport { useAlert } from \"./Alert.js\";\nimport router from \"../router\";\n\n\nexport const useMenuStore = defineStore(\"menu\", {\n  state: () => ({\n    cart: [],\n    item: [],\n    items: [],\n    course: [],\n    orderType: [],\n    defautlMenu: [],\n    aggregatorList: [],\n    aggregatorItem: [],\n    quantity: \"\",\n    comments: \"\",\n    searchTerm: \"\",\n    itemComments: \"\",\n    perPage: 20,\n    currentPage: 1,\n    aggregatorId: null,\n    selectedCourse: null,\n    selectedOrderType: null,\n    selectedAggregator: null,\n    showAll: true,\n    displayAll: true,\n    isAggregator: false,\n    priority: false,\n    showDialog: false,\n    showPriority: false,\n    showDialogCart: false,\n    db: frappe.db(),\n    call: frappe.call(),\n    alert: useAlert(),\n    auth: useAuthStore(),\n    table: useTableStore(),\n    customer: useCustomerStore(),\n    notification: useNotifications(),\n    invoiceData: useInvoiceDataStore(),\n    recentOrders: usetoggleRecentOrder(),\n  }),\n  getters: {\n    filteredItems(state) {\n      if (\n        typeof state.searchTerm !== \"string\" ||\n        state.searchTerm.trim() === \"\"\n      ) {\n        if (state.showAll) {\n          if (state.selectedCourse) {\n            return state.items.filter(\n              (item) => item.course === state.selectedCourse\n            );\n          } else {\n            return state.items; // Return all items if no course is selected\n          }\n        } else {\n          if (state.selectedCourse) {\n            return state.items.filter(\n              (item) =>\n                item.special_dish === 1 && item.course === state.selectedCourse\n            );\n          } else {\n            return state.items.filter((item) => item.special_dish === 1);\n          }\n        }\n      } else {\n        const searchTerm = state.searchTerm.toLowerCase();\n        return state.items.filter(\n          (item) =>\n            typeof item.item_name === \"string\" &&\n            typeof item.item === \"string\" &&\n            (item.item_name.toLowerCase().includes(searchTerm) ||\n              item.item.toLowerCase().includes(searchTerm)) &&\n            (!state.selectedCourse || item.course === state.selectedCourse) &&\n            (state.showAll || item.special_dish === 1)\n        );\n      }\n    },\n    totalPages() {\n      return Math.ceil(this.filteredItems.length / this.perPage);\n    },\n    paginatedItems() {\n      const startIndex = (this.currentPage - 1) * this.perPage;\n      const endIndex = startIndex + this.perPage;\n      return this.filteredItems.slice(startIndex, endIndex);\n    },\n    pageNumbers() {\n      const pageNumbers = [];\n      for (let i = 1; i <= this.totalPages; i++) {\n        pageNumbers.push(i);\n      }\n      return pageNumbers;\n    },\n    setColorForBilledInvoice() {\n      if (\n        this.recentOrders.editPrintedInvoice === 0 ||\n        this.auth.removeTableOrderItem === 1\n      ) {\n        return \"black\";\n      } else if (\n        this.recentOrders.editPrintedInvoice === 1 ||\n        this.auth.removeTableOrderItem === 0\n      ) {\n        return \"gray\";\n      }\n    },\n    grand_total() {\n      return this.cart\n        .reduce((total, item) => {\n          return total + parseFloat(item.rate) * item.qty;\n        }, 0)\n        .toFixed(2);\n    },\n  },\n  actions: {\n    fetchItems() {\n      let order_type = null\n      if (this.auth.cashier) {\n        order_type = this.selectedOrderType;\n      }else{\n        order_type = null\n      }\n      const getMenu = {\n        pos_profile: this.invoiceData.posProfile,\n        order_type:order_type\n      };\n      this.call\n        .get(\"ury.ury_pos.api.getRestaurantMenu\", getMenu)\n        .then((result) => {\n          if (!this.auth.cashier && this.table.tableMenu) {\n            this.items = this.table.tableMenu;\n          } else {\n            this.defautlMenu = result.message.items;\n            this.items = this.defautlMenu;\n          }\n          this.items.forEach((menuItem) => {\n            if (menuItem.special_dish == 1) {\n              this.showPriority = true;\n            } else {\n              this.showAll = true;\n            }\n          });\n        })\n        .catch((error) => {\n          if (error._server_messages) {\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n            this.alert.createAlert(\"Message\", message.message, \"OK\");\n          }\n        });\n      this.db\n        .getDocList(\"URY Menu Course\", {\n          fields: [\"name\"],\n          limit: \"*\",\n        })\n        .then((docs) => {\n          this.course = docs;\n        });\n    },\n    pickOrderType() {\n      this.call\n        .get(\"ury.ury_pos.api.get_select_field_options\")\n        .then((result) => {\n          this.orderType = result.message.filter(\n            (option) => option.name !== \"\"\n          );\n        })\n        .catch((error) => console.error(error));\n    },\n    clearPreviousData() {\n      this.recentOrders.selectedTable = \"\";\n      this.recentOrders.previousOrderdCustomer = \"\"\n      this.recentOrders.selectedStatus = \"Draft\"\n      this.table.invoiceNo = \"\";\n      this.table.selectedTable = \"\";\n      this.invoiceData.invoiceNumber = \"\";\n      this.recentOrders.showOrder = \"\";\n      this.recentOrders.invoiceNumber = \"\";\n      this.recentOrders.recentOrderListItems = [];\n      this.recentOrders.texDetails = [];\n      this.recentOrders.orderType = \"\";\n      this.recentOrders.draftInvoice = \"\";\n      this.recentOrders.netTotal = 0;\n      this.recentOrders.grandTotal = 0;\n      this.recentOrders.invoiceNumber = \"\";\n      this.recentOrders.selectedOrder = [];\n      this.recentOrders.selectedTable = \"\";\n      this.customer.search = \"\";\n      this.recentOrders.restaurantTable = \"\"\n      this.recentOrders.restaurantTable = null\n      this.aggregatorItem = []\n    },\n    orderTypeSelection() {\n      this.clearPreviousData();\n      this.customer.selectedOrderType = this.selectedOrderType;\n      if (\n        this.selectedOrderType === \"Dine In\" &&\n        !this.recentOrders.restaurantTable\n      ) {\n        this.selectedOrderType = null;\n        this.alert.createAlert(\n          \"Message\",\n          \"Dine in is not permitted for takeaway orders.\",\n          \"OK\"\n        );\n      } else {\n        if (this.selectedOrderType !== \"Aggregators\") {\n          this.fetchItems()\n          router.push(\"/Menu\");\n        }\n      }\n\n      if (this.cart.length > 0) {\n        this.alert\n          .createAlert(\n            \"Cart Not Empty\",\n            \"Please clear your cart before selecting an order type.\",\n            \"OK\"\n          )\n          .then(() => {\n            window.location.reload();\n          });\n      } else {\n        if (this.selectedOrderType === \"Aggregators\") {\n          this.call\n            .get(\"ury.ury_pos.api.getAggregator\")\n            .then((result) => {\n              this.aggregatorList = result.message;\n            })\n            .catch((error) => {\n              if (error._server_messages) {\n                const messages = JSON.parse(error._server_messages);\n                const message = JSON.parse(messages[0]);\n                this.alert.createAlert(\"Message\", message.message, \"OK\");\n              }\n            });\n        } else {\n          this.aggregatorItem = \"\";\n          this.selectedAggregator = \"\";\n          this.items = this.defautlMenu;\n        }\n      }\n    },\n    handleAggregatorChange() {\n      if (this.selectedOrderType === \"Aggregators\" && this.cart.length > 0) {\n        this.alert\n          .createAlert(\n            \"Cart Not Empty\",\n            \"Please empty your cart before selecting an aggregator.\",\n            \"OK\"\n          )\n          .then(() => {\n            window.location.reload();\n          });\n      } else {\n        this.customer.search = this.selectedAggregator;\n        const getMenu = {\n          aggregator: this.selectedAggregator,\n        };\n        this.call\n          .get(\"ury.ury_pos.api.getAggregatorItem\", getMenu)\n          .then((result) => {\n            this.aggregatorItem = this.items = result.message;\n            router.push(\"/Menu\");\n            if (result.message) {\n              this.items = result.message;\n            } else {\n              this.items = defautlMenu;\n            }\n          })\n          .catch((error) => {\n            if (error._server_messages) {\n              const messages = JSON.parse(error._server_messages);\n              const message = JSON.parse(messages[0]);\n              this.alert.createAlert(\"Message\", message.message, \"OK\");\n            }\n          });\n      }\n    },\n    itemNameExtract(item_name) {\n      return item_name\n        ? item_name\n          .split(\" \")\n          .map((word) => (word ? word[0].toUpperCase() : \"\"))\n          .join(\"\")\n          .substring(0, 2)\n        : \"\";\n    },\n    updateSearchTerm() {\n      this.currentPage = 1;\n    },\n    getFullImagePath(relativePath) {\n      return `${frappe.url}${relativePath}`;\n    },\n\n    handleSearchInput(event) {\n      this.searchTerm = event.target.value;\n      this.updateSearchTerm();\n    },\n    clearSearch(event) {\n      event.target.value = \"\";\n      this.searchTerm = \"\";\n    },\n    showAllItems() {\n      this.showAll = true;\n      this.priority = false;\n      this.displayAll = true;\n      this.searchTerm = \"\";\n      this.selectedCourse = \"\";\n    },\n    showSpecialItems() {\n      this.priority = true;\n      this.displayAll = false;\n      this.showAll = false;\n    },\n    showModal(item) {\n      this.showDialog = true;\n      this.quantity = item.qty;\n      this.item = item;\n      this.itemComments = item.comment;\n    },\n\n    addToCartAndUpdateQty() {\n      const item = this.item;\n\n      if (\n        this.quantity !== null &&\n        this.quantity !== undefined &&\n        this.quantity !== \"\" &&\n        this.quantity > 0\n      ) {\n        if (!item.qty) {\n          this.$set(item, \"qty\", this.quantity);\n        } else {\n          item.qty = this.quantity;\n          item.comment = this.itemComments;\n        }\n      }\n\n      this.showDialog = false;\n    },\n\n    getitemQty(item) {\n      item.qty = this.cart.qty;\n    },\n    addToCart(item) {\n      const itemIndex = this.cart.findIndex((obj) => obj.item === item.item);\n      const itemIndexExists = itemIndex !== -1;\n\n      if (!itemIndexExists) {\n        item.qty = 1;\n        item.comment = \"\";\n        this.cart.push(item);\n\n        let message = `Added ${item.item} to Cart`;\n        this.notification.createNotification(message);\n      }\n    },\n    incrementItemQuantity(item) {\n      const itemIndex = this.cart.findIndex((obj) => obj.item === item.item);\n      const itemIndexExists = itemIndex !== -1;\n      const posProfile = this.invoiceData.posProfile;\n      let previousOrderItem = this.table.previousOrderdItem;\n      // Check if item exists in previous orders\n      const previousItem = previousOrderItem.find(\n        (previous_item) => previous_item.item_code === item.item\n      );\n\n      let item_qty = itemIndexExists ? this.cart[itemIndex].qty + 1 : 1;\n      // If item exists in previous orders, adjust quantity\n      if (previousItem) {\n        item_qty -= previousItem.qty;\n      }\n      if (itemIndexExists) {\n        item.comment = \"\";\n        this.cart[itemIndex].qty++;\n        let message = `${item.item}'s Qty updated to ${item.qty} in Cart`;\n        this.notification.createNotification(message);\n      } else {\n        item.comment = \"\";\n        this.cart.push({ item: item.item, qty: 1 });\n      }\n    },\n    decrementItemQuantity(item) {\n      const itemIndex = this.cart.findIndex((obj) => obj.item === item.item);\n      const itemIndexExists = itemIndex !== -1;\n      if (itemIndexExists) {\n        this.cart[itemIndex].qty--;\n        this.cart = this.cart.filter((obj) => obj.qty > 0);\n        let message =\n          item.qty > 0\n            ? `${item.item}'s Qty Reduced from Cart Total Qty=${item.qty}`\n            : `${item.item} has been removed from Cart`;\n        this.notification.createNotification(message);\n      }\n    },\n    removeItemFromCart(index) {\n      // Get the item that corresponds to the index\n      const item = this.cart[index];\n      // Set the item's quantity to zero\n      item.qty = 0;\n      this.cart.splice(index, 1);\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/Notification.js",
    "content": "import { defineStore } from \"pinia\";\n\nexport const useNotifications = defineStore(\"notification\", {\n  state: () => ({}),\n  actions: {\n    createNotification(message) {\n      const container = document.createElement(\"div\");\n      container.classList.add(\"fixed\", \"bottom-20\", \"right-5\");\n      document.body.appendChild(container); // Append the container to the body element\n\n      const notif = document.createElement(\"div\");\n      notif.classList.add(\"bg-green-100\", \"text-dark\", \"py-2\", \"px-2\", \"mr-3\");\n      notif.style.borderRadius = \"5px\";\n      notif.style.width = \"400px\";\n      notif.style.height = \"65px\";\n      // Add a media query to adjust the width on smaller screens\n      const mq = window.matchMedia(\"(max-width: 640px)\");\n      if (mq.matches) {\n        notif.style.width = \"300px\";\n      }\n\n      // Create close button\n      const closeBtn = document.createElement(\"span\");\n      closeBtn.innerHTML = `\n          <span class=\"sr-only\">Close</span>\n          <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n          </svg>`;\n      closeBtn.classList.add(\n        \"cursor-pointer\",\n        \"ml-12\",\n        \"absolute\",\n        \"top-4\",\n        \"right-7\"\n      );\n      closeBtn.addEventListener(\"click\", () => {\n        notif.remove();\n        container.remove(); // Remove the container after the notification is removed\n      });\n\n      // Add close button to notification element\n      notif.appendChild(closeBtn);\n\n      // Add message to notification element\n      const messageEl = document.createElement(\"h2\");\n      messageEl.textContent = message;\n      messageEl.classList.add(\"mt-2\");\n\n      // Add message to notification element\n      notif.appendChild(messageEl);\n\n      // Add notification element to the container\n      container.appendChild(notif);\n\n      setTimeout(() => {\n        notif.remove();\n        container.remove(); // Remove the container after the notification is removed\n      }, 900);\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/NotificationModal.js",
    "content": "import { defineStore } from 'pinia';\n\nexport const useNotificationModal = defineStore('notificationModal', {\n  state: () => ({\n    isOpen: false,\n    title: '',\n    message: '',\n    actionText: 'OK',\n    onConfirm: null,\n    onCancel: null,\n    showCancelButton: false\n  }),\n\n  actions: {\n    showModal(options) {\n\n      this.isOpen = true;\n      this.title = options.title || '';\n      this.message = options.message || '';\n      this.actionText = options.actionText || 'OK';\n      this.onConfirm = options.onConfirm;\n      this.onCancel = options.onCancel;\n      this.showCancelButton = options.showCancelButton || false;\n    },\n\n    closeModal() {\n      this.isOpen = false;\n      setTimeout(() => {\n        this.title = '';\n        this.message = '';\n        this.actionText = 'OK';\n        this.onConfirm = null;\n        this.onCancel = null;\n        this.showCancelButton = false;\n      }, 200);\n    },\n\n    handleConfirm() {\n      if (typeof this.onConfirm === 'function') {\n        this.onConfirm();\n      }\n      this.closeModal();\n    },\n\n    handleCancel() {\n      if (typeof this.onCancel === 'function') {\n        this.onCancel();\n      }\n      this.closeModal();\n    }\n  }\n});"
  },
  {
    "path": "urypos/src/stores/Table.js",
    "content": "import { defineStore } from \"pinia\";\nimport router from \"../router\";\nimport { useMenuStore } from \"./Menu.js\";\nimport { useInvoiceDataStore } from \"./invoiceData.js\";\nimport { useAuthStore } from \"./Auth.js\";\nimport { useCustomerStore } from \"./Customer.js\";\nimport { useNotifications } from \"./Notification.js\";\nimport { useAlert } from \"./Alert.js\";\nimport frappe from \"./frappeSdk.js\";\nimport { usetoggleRecentOrder } from \"./recentOrder.js\";\n\n\nexport const useTableStore = defineStore(\"table\", {\n  state: () => ({\n    tables: [],\n    selectedTable: null,\n    previousOrderdItem: [],\n    invoiceNo: \"\",\n    takeAwayTable: 0,\n    alert: useAlert(),\n    previousOrder: [],\n    previousOrderdCustomer: \"\",\n    invoiceData: useInvoiceDataStore(),\n    grandTotal: \"\",\n    notification: useNotifications(),\n    selectedOption: \"\",\n    isTakeAway: \"\",\n    mobileNumber: \"\",\n    showModal: false,\n    isTakeaeay: false,\n    newTable: \"\",\n    showTable: false,\n    transferTable: [],\n    menu: useMenuStore(),\n    tableMenu: [],\n    activeDropdown: null,\n    currentCaptain: null,\n    tableName: \"\",\n    showModalCaptainTransfer: false,\n    showCaptain: false,\n    cashier: null,\n    captain: [],\n    previousWaiter: null,\n    newCaptain: \"\",\n    invoicePrinted: \"\",\n    auth: useAuthStore(),\n    call: frappe.call(),\n    customers: useCustomerStore(),\n    db: frappe.db(),\n    totalMinutes: null,\n    invoiceNumber: null,\n    modifiedTime: null,\n    selectedRoom: null,\n    orderModified: null,\n    menuName: null,\n    rooms: [],\n    recentOrders: usetoggleRecentOrder()\n  }),\n  getters: {\n    filteredTables(state) {\n      return state.tables.filter((table) => table.is_take_away === 0);\n    },\n    takeAway(state) {\n      return state.tables.filter((table) => table.is_take_away === 1);\n    },\n    searchTable() {\n      return this.transferTable.filter((table) => {\n        return table.name.toLowerCase().includes(this.newTable.toLowerCase());\n      });\n    },\n    searchCaptian() {\n      return this.captain.filter((ordeTakers) => {\n        return ordeTakers.name\n          .toLowerCase()\n          .includes(this.newCaptain.toLowerCase());\n      });\n    },\n    toggleTableType(state) {\n      return state.isTakeaeay ? \"translateX(215%)\" : \"translateX(0)\";\n    },\n    tableTypeLabel(state) {\n      return state.isTakeaeay ? \"Takeaway\" : \"Table\";\n    },\n    tableTypeClass(state) {\n      return state.isTakeaeay ? \"text-left ml-1\" : \"text-center ml-2\";\n    },\n  },\n  actions: {\n    fetchRoom() {\n      this.selectedOption = \"Table\";\n      if (this.invoiceData.multipleCashier) {\n        this.call.get(\"ury.ury_pos.api.getRoom\").then((result) => {\n          this.rooms = result.message;\n          const selectedRoom = localStorage.getItem(\"selectedRoom\");\n          if (\n            selectedRoom !== null &&\n            selectedRoom !== \"\" &&\n            selectedRoom !== \"null\"\n          ) {\n            this.selectedRoom = selectedRoom;\n            this.handleRoomChange();\n          }\n\n        });\n      } else {\n        this.db\n          .getDocList(\"URY Room\", {\n            fields: [\"name\", \"branch\"],\n            filters: [[\"branch\", \"like\", this.invoiceData.branch]],\n            limit: \"*\",\n          })\n          .then((docs) => {\n            this.rooms = docs;\n            const selectedRoom = localStorage.getItem(\"selectedRoom\");\n            if (\n              selectedRoom !== null &&\n              selectedRoom !== \"\" &&\n              selectedRoom !== \"null\"\n            ) {\n              this.selectedRoom = selectedRoom;\n              this.handleRoomChange();\n            } else {\n              this.db\n                .getDocList(\"URY Restaurant\", {\n                  fields: [\"branch\", \"default_room\"],\n                  filters: [[\"branch\", \"like\", this.invoiceData.branch]],\n                })\n                .then((docs) => {\n                  let room = docs.find((room) => room.default_room);\n                  this.selectedRoom = room ? room.default_room : null;\n\n                  this.handleRoomChange();\n                });\n            }\n\n          })\n          .catch((error) => console.error(error));\n      }\n    },\n    async handleRoomChange() {\n      localStorage.setItem(\"selectedRoom\", this.selectedRoom);\n      await this.fetchTable();\n      await this.getMenu();\n      if (this.invoiceData.multipleCashier) {\n        this.getCashier()\n      }\n    },\n    getCashier() {\n      const getCashier = {\n        room: this.selectedRoom,\n      };\n      this.call.get(\"ury.ury_pos.api.getCashier\", getCashier).then((result) => {\n        this.cashier = result.message\n      });\n    },\n    fetchTable() {\n      this.db\n        .getDocList(\"URY Table\", {\n          fields: [\n            \"name\",\n            \"occupied\",\n            \"latest_invoice_time\",\n            \"is_take_away\",\n            \"restaurant_room\",\n            \"table_shape\",\n            \"no_of_seats\",\n            \"layout_x\",\n            \"layout_y\",\n            \"minimum_seating\",\n          ],\n          filters: [[\"restaurant_room\", \"=\", this.selectedRoom]],\n          limit: \"*\",\n        })\n        .then((tables) => {\n          this.tables = tables.sort((a, b) => {\n            return a.name.localeCompare(b.name, undefined, {\n              numeric: true,\n              sensitivity: \"base\",\n            });\n          });\n        });\n    },\n    async getMenu() {\n      const getMenuIem = {\n        room: this.selectedRoom,\n        pos_profile: this.invoiceData.posProfile,\n      };\n      try {\n        await this.call\n          .get(\"ury.ury_pos.api.getRestaurantMenu\", getMenuIem)\n          .then((result) => {\n            this.tableMenu = result.message.items;\n            this.menuName = result.message.name;\n            this.orderModified = result.message.modified;\n            this.menu.fetchItems();\n          });\n      } catch (error) {\n        if (error._server_messages) {\n          const messages = JSON.parse(error._server_messages);\n          const message = JSON.parse(messages[0])\n          this.alert.createAlert(\"Message\", message.message, \"OK\");\n        }\n      }\n    },\n    toggleTableTypeSwitch() {\n      this.isTakeaeay = !this.isTakeaeay;\n    },\n    tableSearch() {\n      this.db\n        .getDocList(\"URY Table\", {\n          filters: [[\"occupied\", \"like\", \"0%\"]],\n          limit: \"*\",\n        })\n        .then((table) => {\n          this.transferTable = table;\n        })\n        .catch((error) => {\n          console.error(error);\n        });\n    },\n    fetchCaptain() {\n      this.db\n        .getDocList(\"User\", {\n          fields: [\"name\"],\n          limit: \"*\",\n        })\n        .then((docs) => {\n          this.captain = docs;\n        })\n        .catch((error) => console.error(error));\n    },\n    async toggleDropdown(index) {\n      this.tableName = index;\n      if (this.activeDropdown === index) {\n        this.activeDropdown = null;\n      } else {\n        this.activeDropdown = index;\n      }\n      await this.invoiceNumberFetching();\n    },\n    hideDropdown() {\n      this.activeDropdown = null;\n    },\n    selectTable(tables) {\n      this.newTable = tables.name;\n      this.showTable = false;\n    },\n    selectcaptain(captain) {\n      this.newCaptain = captain.name;\n      this.showCaptain = false;\n    },\n    getTimeDifference(table) {\n      const now = new Date();\n      let tableTime = \"00:00:00\";\n      if (table && table.occupied === 1 && table.latest_invoice_time) {\n        tableTime = table.latest_invoice_time;\n      }\n      const [tableHours, tableMinutes, tableSeconds] = tableTime.split(\":\");\n      const tableDate = new Date(\n        now.getFullYear(),\n        now.getMonth(),\n        now.getDate(),\n        tableHours,\n        tableMinutes,\n        tableSeconds\n      );\n      const timeDifferenceInMs = now - tableDate;\n      const secondsDifference = Math.floor(timeDifferenceInMs / 1000);\n      const minutesDifference = Math.floor(secondsDifference / 60);\n      const hoursDifference = Math.floor(minutesDifference / 60);\n      const formattedTimeDifference = `${hoursDifference}:${minutesDifference % 60\n        }`;\n      return formattedTimeDifference;\n    },\n    getBadgeType(table) {\n      if (table.occupied != 1 && table.name !== this.selectedTable) {\n        return \"green\";\n      } else if (table.name === this.selectedTable) {\n        return \"default\";\n      } else if (table.occupied === 1 && table.name !== this.selectedTable) {\n        const timeDifference = this.getTimeDifference(table);\n        const [hours, minutes] = timeDifference.split(\":\");\n        const totalMinutes = parseInt(hours) * 60 + parseInt(minutes);\n        if (totalMinutes > this.invoiceData.tableAttention) {\n          return \"red\";\n        } else {\n          return \"yellow\";\n        }\n      }\n    },\n    getBadgeText(table) {\n      if (table.occupied != 1 && table.name !== this.selectedTable) {\n        return \"Free\";\n      } else if (table.name === this.selectedTable) {\n        return \"Active\";\n      } else if (table.occupied === 1 && table.name !== this.selectedTable) {\n        const timeDifference = this.getTimeDifference(table);\n        const [hours, minutes] = timeDifference.split(\":\");\n        const totalMinutes = parseInt(hours) * 60 + parseInt(minutes);\n        if (totalMinutes > this.invoiceData.tableAttention) {\n          return \"Attention\";\n        } else {\n          return \"Occupied\";\n        }\n      }\n    },\n    async addToSelectedTables(table) {\n      this.selectedTable = table.name;\n      this.takeAwayTable = 0;\n\n      if (table.is_take_away === 1) {\n        this.isTakeAway = \"Take Away\";\n        this.takeAwayTable = 1;\n      }\n      let previousOrderdNumberOfPax = \"\";\n      this.previousOrderdItem = [];\n      this.recentOrders.modifiedTime = \"\"\n      this.recentOrders.pastOrderdItem = []\n      this.invoiceNo = \"\";\n      let items = this.tableMenu;\n      items.forEach((item) => {\n        item.qty = \"\";\n      });\n      let cart = this.menu.cart;\n      cart.splice(0, cart.length);\n      const getPreviousOrder = {\n        table: this.selectedTable,\n      };\n      this.call\n        .get(\n          \"ury.ury.doctype.ury_order.ury_order.get_order_invoice\",\n          getPreviousOrder\n        )\n        .then((result) => {\n          this.previousOrder = result.message;\n          this.invoicePrinted = this.previousOrder.invoice_printed;\n          this.menu.comments = this.previousOrder.custom_comments;\n          this.modifiedTime = this.previousOrder.modified;\n          this.grandTotal = this.previousOrder.grand_total;\n          this.mobileNumber = this.previousOrder.mobile_number;\n          this.invoiceNo = this.previousOrder.name;\n          this.previousWaiter = this.previousOrder.waiter;\n          if (this.invoiceNo) {\n            if (\n              !this.auth.hasAccess &&\n              !this.auth.cashier &&\n              this.auth.sessionUser !== this.previousOrder.waiter\n            ) {\n              this.alert\n                .createAlert(\n                  \"Message\",\n                  \"Table is assigned to \" + this.previousOrder.waiter,\n                  \"OK\"\n                )\n                .then(() => {\n                  router.push(\"/Table\").then(() => {\n                    window.location.reload();\n                  });\n                });\n            } else {\n              this.notification.createNotification(\"Past Order Fetched\");\n            }\n          } else {\n            router.push(\"/Menu\");\n          }\n          this.previousOrderdItem = this.previousOrder.items;\n          this.previousOrderdCustomer = this.previousOrder.customer;\n          previousOrderdNumberOfPax = this.previousOrder.no_of_pax;\n          if (this.previousOrderdCustomer) {\n            this.customers.search = this.previousOrderdCustomer;\n            this.customers.numberOfPax = previousOrderdNumberOfPax;\n            this.customers.fectchCustomerFavouriteItem();\n          } else {\n            this.customers.search = \"\";\n            this.customers.numberOfPax = \"\";\n            this.customers.customerFavouriteItems = \"\";\n            this.customers.newCustomerMobileNo = \"\"\n          }\n\n          items.forEach((item) => {\n            const previousItem =\n              this.previousOrderdItem &&\n              this.previousOrderdItem.find(\n                (previousItem) => previousItem.item_code === item.item\n              );\n            if (previousItem && !item.qty) {\n              const itemIndex = cart.findIndex((obj) => obj.item === item.item);\n              const itemIndexExists = itemIndex !== -1;\n              if (!itemIndexExists) {\n                item.qty = previousItem.qty;\n                item.comment = previousItem.comment;\n                cart.push(item);\n              }\n            }\n          });\n          if (this.previousOrderdItem && this.previousOrderdItem.length > 0) {\n            this.previousOrderdItem.forEach((previousItem) => {\n              const existsInMenu = items.some(item => item.item === previousItem.item_code);\n              const existsInCart = cart.some(item => item.item === previousItem.item_code);\n\n              if (!existsInMenu && !existsInCart) {\n                // Item no longer in menu but was in previous order - add it to cart\n                cart.push({\n                  item: previousItem.item_code,\n                  item_name: previousItem.item_name,\n                  rate: previousItem.rate,\n                  qty: previousItem.qty,\n                  comment: previousItem.comment\n                });\n              }\n            });\n          }\n        })\n        .catch((error) => console.error(error));\n    },\n    routeToCart(table) {\n      this.addToSelectedTables(table);\n      router.push(\"/Cart\");\n    },\n    routeToMenu(table) {\n      this.addToSelectedTables(table);\n      router.push(\"/Menu\");\n    },\n    async invoiceNumberFetching() {\n      const tableInvoiceNumber = {\n        table: this.tableName,\n      };\n      try {\n        const result = await this.call.get(\n          \"ury.ury.doctype.ury_order.ury_order.get_order_invoice\",\n          tableInvoiceNumber\n        );\n        this.invoiceNumber = result.message.name;\n        this.currentCaptain = result.message.waiter;\n      } catch (error) {\n        console.error(error._server_messages);\n      }\n    },\n    tableTransfer: async function () {\n      await this.invoiceNumberFetching();\n      const transferTable = {\n        table: this.tableName,\n        newTable: this.newTable,\n        invoice: this.invoiceNumber,\n      };\n      this.call\n        .post(\n          \"ury.ury.doctype.ury_order.ury_order.table_transfer\",\n          transferTable\n        )\n        .then(() => {\n          window.location.reload();\n        })\n        .catch((error) => {\n          if (error._server_messages) {\n            this.newTable = \"\";\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n            this.alert.createAlert(\"Message\", message.message, \"OK\");\n          }\n        });\n    },\n    captianTransfer: async function () {\n      await this.invoiceNumberFetching();\n      if (this.invoiceNumber) {\n        const transferCaptain = {\n          currentCaptain: this.currentCaptain,\n          newCaptain: this.newCaptain,\n          invoice: this.invoiceNumber,\n        };\n        this.call\n          .post(\n            \"ury.ury.doctype.ury_order.ury_order.captain_transfer\",\n            transferCaptain\n          )\n          .then(() =>\n            this.notification.createNotification(\n              \"Captain Transferred Successfully\"\n            )\n          )\n          .then(() => window.location.reload())\n          .catch((error) => {\n            if (error._server_messages) {\n              const messages = JSON.parse(error._server_messages);\n              const message = JSON.parse(messages[0]);\n              this.alert.createAlert(\"Message\", message.message, \"OK\");\n            }\n          });\n      }\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/bottomTabs.js",
    "content": "import { defineStore } from \"pinia\";\nimport { useAuthStore } from \"./Auth.js\";\nimport router from \"../router\";\nimport { useAlert } from \"./Alert.js\";\nimport { useTableStore } from \"./Table.js\";\nimport { useMenuStore } from \"./Menu.js\";\n\nexport const tabFunctions = defineStore(\"tabClick\", {\n  state: () => ({\n    auth: useAuthStore(),\n    alert: useAlert(),\n    menu: useMenuStore(),\n    table: useTableStore(),\n  }),\n  getters: {\n    isLoginPage() {\n      return router.currentRoute.value.path === \"/login\";\n    },\n    currentTab() {\n      return router.currentRoute.value.path;\n    },\n  },\n  actions: {\n    checkActiveTable() {\n      if (!this.table.selectedTable) {\n        this.alert\n          .createAlert(\n            \"No Active Table\",\n            \"You have not selected an active table\",\n            \"Ok\"\n          )\n          .then(() => {\n            router.push(\"/Table\");\n          });\n      }\n    },\n    clickMenuTab() {\n      if (!this.auth.cashier && !this.table.selectedTable) {\n        this.alert\n          .createAlert(\n            \"No Active Table\",\n            \"You have not selected an active table\",\n            \"Ok\"\n          )\n          .then(() => {\n            router.push(\"/Table\");\n          });\n      }\n      if (this.auth.cashier && !this.menu.selectedOrderType) {\n        this.alert\n          .createAlert(\n            \"No Order Type\",\n            \"Please select an Order Type\",\n            \"Ok\"\n          )\n          .then(() => {\n            router.push(\"/Table\");\n          });\n      }\n      if (\n        this.auth.cashier &&\n        this.menu.selectedOrderType === \"Aggregators\" &&\n        !this.menu.selectedAggregator\n      ) {\n        this.alert\n          .createAlert(\"No Aggregator\", \"Please select an Aggregator\", \"Ok\")\n          .then(() => {\n            router.push(\"/Table\");\n          });\n      }\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/frappeSdk.js",
    "content": "import { FrappeApp } from \"frappe-js-sdk\";\n\nlet host = window.location.hostname;\nlet port = window.location.port;\nlet protocol = window.location.protocol;\nlet url = port ? `${protocol}//${host}:${port}` : `${protocol}//${host}:${port}`;\n\nexport const frappe = new FrappeApp(url);\n\nexport default frappe;\n"
  },
  {
    "path": "urypos/src/stores/invoiceData.js",
    "content": "import { defineStore } from \"pinia\";\nimport router from \"../router\";\nimport { useTableStore } from \"./Table.js\";\nimport { useMenuStore } from \"./Menu.js\";\nimport { useCustomerStore } from \"./Customer.js\";\nimport { useNotifications } from \"./Notification.js\";\nimport { usetoggleRecentOrder } from \"./recentOrder.js\";\nimport { useAlert } from \"./Alert.js\";\nimport { useNotificationModal } from './NotificationModal';\nimport { useAuthStore } from \"./Auth.js\";\nimport frappe from \"./frappeSdk.js\";\n\nimport {\n  printWithQz,\n  loadQzPrinter,\n  disconnectQzPrinter,\n} from \"./utils/PrintWithQz\";\n\nexport const useInvoiceDataStore = defineStore(\"invoiceData\", {\n  state: () => ({\n    waiter: \"\",\n    cashier: \"\",\n    warehouse: \"\",\n    posProfile: \"\",\n    enableKotReprint:0,\n    defaultModeOfPayment: \"Cash\",\n    owner:null,\n    branch: null,\n    printer: null,\n    qz_host: null,\n    company: null,\n    currency: null,\n    qz_print: null,\n    paidLimit: null,\n    print_type: null,\n    grandTotal: null,\n    modifiedTime: null,\n    print_format: null,\n    cancelReason: null,\n    invoiceNumber: null,\n    multipleCashier:null,\n    tableInvoiceNo: null,\n    tableAttention: null,\n    modeOfPaymentList: null,\n    disableRoundedTotal: null,\n    showUpdateButtton: true,\n    isChecked: false,\n    isPrinting: false,\n    showDialog: false,\n    kotPrinting: false,\n    editOrderType:false,\n    enableDiscount: false,\n    invoiceUpdating: false,\n    cancelInvoiceFlag: false,\n    invoiceDetails: [],\n    previousOrderItem: [],\n    db: frappe.db(),\n    call: frappe.call(),\n    alert: useAlert(),\n    auth: useAuthStore(),\n    menu: useMenuStore(),\n    table: useTableStore(),\n    customers: useCustomerStore(),\n    notification: useNotifications(),\n    recentOrders: usetoggleRecentOrder(),\n    notificationModal: useNotificationModal(),\n  }),\n  actions: {\n    async fetchInvoiceDetails() {\n      try {\n        await this.call.get(\"ury.ury_pos.api.getPosProfile\").then((result) => {\n          this.invoiceDetails = result.message;\n          this.tableAttention = this.invoiceDetails.tableAttention;\n          this.warehouse = this.invoiceDetails.warehouse;\n          this.posProfile = this.invoiceDetails.pos_profile;\n          this.waiter = this.invoiceDetails.waiter;\n          this.cashier = this.invoiceDetails.cashier;\n          this.owner = this.invoiceDetails.owner\n          this.branch = this.invoiceDetails.branch;\n          this.company = this.invoiceDetails.company;\n          this.print_format = this.invoiceDetails.print_format;\n          this.qz_print = this.invoiceDetails.qz_print;\n          this.qz_host = this.invoiceDetails.qz_host;\n          this.print_type = this.invoiceDetails.print_type;\n          this.printer = this.invoiceDetails.printer;\n          this.paidLimit = this.invoiceDetails.paid_limit;\n          this.disableRoundedTotal = this.invoiceDetails.disable_rounded_total;\n          this.enableDiscount = this.invoiceDetails.enable_discount;\n          this.enableKotReprint=this.invoiceDetails.enable_kot_reprint;\n          this.multipleCashier=this.invoiceDetails.multiple_cashier\n          this.editOrderType=this.invoiceDetails.edit_order_type\n          if (this.qz_host) {\n            loadQzPrinter(this.qz_host);\n          }\n          this.db\n            .getDoc(\"Company\", this.company)\n            .then((doc) => {\n              this.db\n                .getDoc(\"Currency\", doc.default_currency)\n                .then((currency) => {\n                  this.currency = currency.symbol;\n                })\n                .catch((error) => {\n                  if (error._server_messages) {\n                    this.alert.createAlert(\n                      \"Message\",\n                      \"You do not have Read or Select Permissions for Currency\",\n                      \"OK\"\n                    );\n                  }\n                });\n            })\n            .catch((error) => {\n              if (error._server_messages) {\n                this.alert.createAlert(\n                  \"Message\",\n                  \"You do not have Read or Select Permissions for Company\",\n                  \"OK\"\n                );\n              }\n            });\n        });\n      } catch (error) {\n        if (error._server_messages) {\n          const messages = JSON.parse(error._server_messages);\n          const message = JSON.parse(messages[0]);\n          this.alert.createAlert(\"Message\", message.message, \"OK\");\n        }\n      }\n      this.call\n        .get(\"ury.ury_pos.api.getModeOfPayment\")\n        .then((result) => {\n          this.modeOfPaymentList = result.message;\n        })\n        .catch((error) => {\n          // console.error(error)\n        });\n    },\n\n    // Method for creating an invoice\n    async invoiceCreation() {\n      this.showUpdateButtton = false;\n      this.invoiceUpdating = true;\n      let selectedTables = \"\";\n      let cart = this.menu.cart;\n      const customerName = this.customers.search;\n      const ordeType =\n        this.menu.selectedOrderType || this.recentOrders.pastOrderType;\n      const numberOfPax = this.customers.numberOfPax;\n      let invoice =\n        this.recentOrders.draftInvoice ||\n        this.table.invoiceNo ||\n        this.invoiceNumber ||\n        null;\n      let lastInvoice =\n        this.invoiceNumber ||\n        this.recentOrders.draftInvoice ||\n        this.table.invoiceNo ||\n        null;\n      let cashier= this.table.cashier ||\n        this.cashier ||\n        this.cashier;\n\n      selectedTables =\n        this.table.selectedTable || this.recentOrders.restaurantTable;\n      const cartCopy = JSON.parse(JSON.stringify(cart));\n      let waiter = null\n      if (lastInvoice) {\n        waiter = this.table.previousWaiter !== null &&\n          this.table.previousWaiter !== undefined\n          ? this.table.previousWaiter\n          : this.recentOrders.recentWaiter !== null &&\n            this.recentOrders.recentWaiter !== undefined\n            ? this.recentOrders.recentWaiter\n            : this.waiter;\n      } else {\n        waiter = this.waiter;\n      }\n      if (this.recentOrders.modifiedTime){\n        this.modifiedTime =this.recentOrders.modifiedTime\n      }\n      else{\n        this.modifiedTime =this.table.modifiedTime\n      }\n\n      // Check for modifications in existing invoice\n      if (invoice) {\n        let pastOrderdItem = [];\n        if (this.table.previousOrderdItem?.length) {\n          pastOrderdItem = this.table.previousOrderdItem;\n        } else if (this.recentOrders.pastOrderdItem?.length) {\n          pastOrderdItem = this.recentOrders.pastOrderdItem;\n        }\n        \n        const originalItems = {};\n        pastOrderdItem.forEach(item => {\n          originalItems[item.item_code] = {\n            qty: item.qty,\n            name: item.item_name\n          };\n        });\n        \n        const currentItems = {};\n        this.menu.cart.forEach(item => {\n          currentItems[item.item] = {\n            qty: item.qty,\n            name: item.item_name\n          };\n        });\n        \n        const removedItems = Object.keys(originalItems).filter(\n          itemCode => !currentItems[itemCode]\n        );\n    \n        const reducedQtyItems = [];\n        Object.entries(originalItems).forEach(([itemCode, itemData]) => {\n          if (currentItems[itemCode] && currentItems[itemCode].qty < itemData.qty) {\n            reducedQtyItems.push(\n              `${itemData.name} (qty reduced from ${itemData.qty} to ${currentItems[itemCode].qty})`\n            );\n          }\n        });\n    \n        if (removedItems.length > 0 || reducedQtyItems.length > 0) {\n          this.invoiceUpdating = false;\n                \n          let errorMsg = [];\n          if (removedItems.length > 0) {\n            const removedItemNames = removedItems.map(\n              itemCode => originalItems[itemCode].name\n            );\n            errorMsg.push(`Removed items: ${removedItemNames.join(', ')}\\n`);\n          }\n          if (reducedQtyItems.length > 0) {\n            errorMsg.push(`Modified quantities: ${reducedQtyItems.join(',\\n')}`);\n          }\n    \n          // Show confirmation modal and wait for user response\n          await new Promise((resolve, reject) => {\n            this.notificationModal.showModal({\n              title: \"Are You Sure to remove these items?\",\n              message: errorMsg.join('\\n'),\n              actionText: \"Yes\",\n              showCancelButton: true,\n              onConfirm: () => {\n                this.invoiceUpdating = true;\n                this.showUpdateButtton = true;\n                resolve();\n              },\n              onCancel: () => {\n                this.showUpdateButtton = true;\n                this.invoiceUpdating = false;\n                reject('User cancelled the operation');\n              }\n            });\n          });\n        }\n      }\n    \n      // Only proceed with API call if no rejection occurred\n      const creatingInvoice = {\n        table: selectedTables,\n        customer: customerName,\n        items: cart,\n        no_of_pax: numberOfPax,\n        mode_of_payment: this.defaultModeOfPayment,\n        cashier: cashier,\n        owner:this.owner,\n        waiter: waiter,\n        last_modified_time: this.modifiedTime,\n        pos_profile: this.posProfile,\n        invoice: invoice,\n        aggregator_id: this.menu.aggregatorId,\n        order_type: ordeType,\n        last_invoice: lastInvoice,\n        comments: this.menu.comments,\n        room: this.table.selectedRoom,\n      };\n      if (!this.auth.cashier && !numberOfPax && this.table.takeAwayTable == 0) {\n        this.alert.createAlert(\n          \"Message\",\n          \"Please Select Customer / No of Pax\",\n          \"OK\"\n        );\n        this.showUpdateButtton = true;\n        this.invoiceUpdating = false;\n        return;\n      }\n    \n      if (!this.auth.cashier && !selectedTables) {\n        this.alert.createAlert(\"Message\", \"Please Select a Table\", \"OK\");\n        this.showUpdateButtton = true;\n        this.invoiceUpdating = false;\n        return;\n      }\n    \n      if (this.auth.cashier && !ordeType && !selectedTables) {\n        this.alert.createAlert(\"Message\", \"Please Select Order Type\", \"OK\");\n        this.showUpdateButtton = true;\n        this.invoiceUpdating = false;\n        return;\n      }\n    \n      try {\n        const response = await this.call.post(\n          \"ury.ury.doctype.ury_order.ury_order.sync_order\",\n          creatingInvoice\n        );\n    \n        this.showUpdateButtton = true;\n        if (response.message.status === \"Failure\") {\n          const alert = response._server_messages;\n          const messages = JSON.parse(alert);\n          const message = JSON.parse(messages[0]);\n    \n          await this.alert.createAlert(\"Message\", message.message, \"OK\");\n          await router.push(\"/Table\");\n          window.location.reload();\n          return;\n        }\n    \n        // Handle successful response\n        this.invoiceNumber = response.message.name;\n        this.grandTotal = response.message.grand_total;\n        this.notification.createNotification(\"Order Update\");\n        this.table.fetchTable();\n        \n        let items = this.menu.items;\n        // items.forEach((item) => {\n        //   item.comment = \"\";\n        // });\n        \n        this.table.previousOrderdItem = response.message.items;\n        this.recentOrders.pastOrderdItem = response.message.items;\n        this.previousOrderItem.splice(0, this.previousOrderItem.length);\n        this.previousOrderItem.splice(0, this.previousOrderItem.length, ...cartCopy);\n        this.invoiceUpdating = false;\n        this.table.modifiedTime = response.message.modified;\n        this.recentOrders.modifiedTime = response.message.modified;\n        if (this.auth.cashier) {\n          this.clearDataAfterUpdate();\n          await router.push(\"/recentOrder\");\n          this.recentOrders.viewRecentOrder(response.message);\n        }\n      } catch (error) {\n        this.showUpdateButtton = true;\n        this.invoiceUpdating = false;\n        if (error === 'User cancelled the operation') {\n          return; // Silently handle cancellation\n        }\n        if (error._server_messages) {\n          const messages = JSON.parse(error._server_messages);\n          const message = JSON.parse(messages[0]);\n          await this.alert.createAlert(\"Message\", message.message, \"OK\");\n        }\n      }\n    },\n\n    clearDataAfterUpdate() {\n      this.menu.items.forEach((item) => {\n        item.comment = \"\";\n        item.qty = \"\";\n      });\n      this.table.cashier=\"\"\n      this.table.takeAwayTable = 0;\n      this.recentOrders.restaurantTable = \"\";\n      this.table.selectedTable = \"\";\n      this.customers.numberOfPax = \"\";\n      this.customers.newCustomerMobileNo=\"\"\n      this.menu.cart = [];\n      this.recentOrders.draftInvoice = \"\";\n      this.menu.selectedAggregator = \"\";\n      this.invoiceNumber = \"\";\n      this.tableInvoiceNo = \"\";\n      this.customers.customerFavouriteItems = \"\";\n      this.customers.search = \"\";\n      this.recentOrders.pastOrderType = \"\";\n      this.recentOrders.showOrder = false;\n      this.recentOrders.invoiceNumber = \"\";\n      this.recentOrders.setBackground = \"\";\n      this.recentOrders.recentOrderListItems = [];\n      this.recentOrders.texDetails = [];\n      this.recentOrders.orderType = \"\";\n      this.recentOrders.netTotal = 0;\n      this.recentOrders.payments = [];\n      this.recentOrders.grandTotal = 0;\n      this.recentOrders.paidAmount = 0;\n      this.recentOrders.billAmount = 0;\n      this.menu.aggregatorItem = []\n      this.recentOrders.invoiceNumber = \"\";\n      this.recentOrders.selectedOrder = [];\n      this.recentOrders.selectedTable = \"\";\n      this.customers.selectedOrderType = \"\";\n      this.menu.selectedOrderType = \"\";\n    },\n    billing(table) {\n      let tables = table.name;\n      const getOrderInvoice = {\n        table: tables,\n      };\n      this.call\n        .get(\n          \"ury.ury.doctype.ury_order.ury_order.get_order_invoice\",\n          getOrderInvoice\n        )\n        .then((result) => {\n          this.tableInvoiceNo = result.message.name;\n          if (\n            !this.auth.hasAccess &&\n            !this.auth.cashier &&\n            this.auth.sessionUser !== result.message.waiter\n          ) {\n            this.alert.createAlert(\n              \"Message\",\n              \"Printing is Blocked Table is assigned to \" +\n              result.message.waiter,\n              \"OK\"\n            );\n          } else {\n            this.isPrinting = true;\n            this.printFunction();\n          }\n        })\n        .catch((error) => console.error(error));\n    },\n    kotReprint() {\n      this.kotPrinting=true;\n      let invoice =\n        this.recentOrders.draftInvoice ||\n        this.table.invoiceNo ||\n        this.invoiceNumber ||\n        null;\n      \n      const invoiceData = {\n        invoice_number: invoice,\n      };\n      this.call\n        .get(\"ury.ury.api.ury_kot_reprint.reprint_kot\", invoiceData)\n        .then((result) => {\n          if (result.message === \"Success\") {\n            this.kotPrinting=false;\n            this.notification.createNotification(\"KOT Reprint Successful\");\n          }\n        })\n        .catch((error) =>{\n          console.error(error);\n          this.kotPrinting=false;\n          if (error._server_messages) {\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n             this.alert.createAlert(\"Message\", message.message, \"OK\");\n          }\n        } );\n\n\n    },\n    printFunction: async function () {\n      this.isPrinting = true;\n      let invoiceNo =\n        this.recentOrders.invoiceNumber ||\n        this.tableInvoiceNo ||\n        this.invoiceNumber;\n      try {\n        if (this.print_type === \"qz\") {\n          const printHTML = {\n            doc: \"POS Invoice\",\n            name: invoiceNo,\n            print_format: this.print_format,\n            _lang: \"en\",\n          };\n          const result = await this.call.get(\n            \"frappe.www.printview.get_html_and_style\",\n            printHTML\n          );\n          if (!result?.message?.html) {\n            this.isPrinting = false;\n            this.alert.createAlert(\n              \"Message\",\n              \"Error while getting the HTML document to print for QZ\",\n              \"OK\"\n            );\n            return;\n          }\n\n          const print = await printWithQz(this.qz_host, result?.message?.html);\n\n          if (print === \"printed\") {\n            const updateSuccess = await this.updatePrintTable(invoiceNo);\n            this.isPrinting = false\n            if (!updateSuccess) {\n              this.notification.createNotification(\n                \"Print successful but failed to update status\"\n              );\n            }\n          }\n        } else if (this.print_type === \"network\") {\n          if (this.auth.cashier && !this.multipleCashier) {\n            const sendObj = {\n              doctype: \"POS Invoice\",\n              name: invoiceNo,\n              printer_setting: this.printer,\n              print_format: this.print_format,\n            };\n            const printingCall = async () => {\n              const res = await this.call.post(\n                \"ury.ury.api.ury_print.network_printing\",\n                sendObj\n              );\n              return res.message;\n            };\n            let i = 0;\n            let errorMessage = \"\";\n            do {\n              const res = await printingCall();\n              if (res === \"Success\") {\n                this.notification.createNotification(\"Print Successful\");\n                const sendObj = {\n                  invoice: invoiceNo,\n                };\n                await this.call\n                  .post(\"ury.ury.api.ury_print.qz_print_update\", sendObj)\n                  .then(() => {\n                    window.location.reload();\n                    return 200;\n                  });\n              }\n              errorMessage = res;\n              i++;\n            } while (i < 1);\n            throw {\n              alert: this.alert.createAlert(\n                \"Message\",\n                `Message:${errorMessage}`,\n                \"OK\"\n              ),\n              custom: (this.isPrinting = false),\n            };\n          } else {\n            const networkPrint = {\n              invoice_id: invoiceNo,\n              pos_profile: this.posProfile,\n            };\n            const networkPrintPrintingCall = async () => {\n              const res = await this.call.post(\n                \"ury.ury.api.ury_print.select_network_printer\",\n                networkPrint\n              );\n              return res.message;\n            };\n            let i = 0;\n            let errorMessage = \"\";\n            do {\n              const res = await networkPrintPrintingCall();\n              if (res === \"Success\") {\n                this.notification.createNotification(\"Print Successful\");\n                const sendObj = {\n                  invoice: invoiceNo,\n                };\n                await this.call\n                  .post(\"ury.ury.api.ury_print.qz_print_update\", sendObj)\n                  .then(() => {\n                    window.location.reload();\n                    return 200;\n                  });\n              }\n              errorMessage = res;\n              i++;\n            } while (i < 1);\n            throw {\n              alert: this.alert.createAlert(\n                \"Message\",\n                `Message:${errorMessage}`,\n                \"OK\"\n              ),\n              custom: (this.isPrinting = false),\n            };\n          }\n        } else {\n          // Socket printing using printview redirection\n          const url = `/printview?doctype=POS Invoice&name=${invoiceNo}&format=${this.print_format}&no_letterhead=1&settings={}&letterhead=No Letterhead&trigger_print=1&_lang=en`;\n          window.open(url, \"_blank\", \"noopener,noreferrer\");\n          try {\n            await this.call.post(\"ury.ury.api.ury_print.qz_print_update\", {\n              invoice: invoiceNo,\n            });\n          } catch (err) {\n            console.error(\"Failed to update print status for socket print\", err);\n          }\n          this.notification.createNotification(\"Print triggered\");\n          this.isPrinting = false;\n        }\n      } catch (e) {\n        if (e?.custom) {\n          this.isPrinting = false;\n\n          return this.alert.createAlert(\"Error\", e?.title, \"OK\");\n        }\n      }\n    },\n    async updatePrintTable(invoiceNo, maxRetries = 3) {\n      let retryCount = 0;\n\n      const tryUpdate = async () => {\n        try {\n          const updatePrintTable = {\n            invoice: invoiceNo,\n          };\n\n          const response = await this.call.post(\n            \"ury.ury.api.ury_print.qz_print_update\",\n            updatePrintTable\n          );\n          if (response.message.status === \"Success\") {\n            this.notification.createNotification(\"Print and Update Successful\");\n            window.location.reload();\n            return true;\n          } else {\n            this.isPrinting = false\n            throw new Error(response.message);\n          }\n        } catch (error) {\n          console.error(`Update attempt ${retryCount + 1} failed:`, error);\n          return false;\n        }\n      };\n\n      while (retryCount < maxRetries) {\n        const success = await tryUpdate();\n        if (success) {\n          return true;\n        }\n        retryCount++;\n        if (retryCount < maxRetries) {\n          // Wait for 1 second before retrying\n          await new Promise(resolve => setTimeout(resolve, 1000));\n        }\n      }\n\n      this.alert.createAlert(\n        \"Error\",\n        \"Failed to update print status after multiple attempts\",\n        \"OK\"\n      );\n      return false;\n    },\n\n    loadPrinter: async function (qz_host) {\n      try {\n        const res = await loadQzPrinter(url, qz_host);\n        print(qz_host);\n        if (res === \"success\")\n          this.notification.createNotification(\"Printer loaded\");\n      } catch (err) {\n        this.alert.createAlert(\"Message\", err.message, \"OK\");\n      }\n    },\n\n    showCancelInvoiceModal() {\n      this.call\n        .get(\"ury.ury.api.button_permission.cancel_check\")\n        .then((result) => {\n          if (result.message === true) {\n            this.cancelInvoiceFlag = true;\n            this.cancelReason = \"\";\n          } else {\n            this.alert.createAlert(\n              \"Message\",\n              \"You don't Have Permission to Cancel \",\n              \"OK\"\n            );\n            this.cancelInvoiceFlag = false;\n            this.cancelReason = \"\";\n          }\n        })\n        .catch((error) => {\n          // console.error(error)\n        });\n    },\n    cancelInvoice: async function () {\n      const recentOrders = usetoggleRecentOrder();\n      let invoiceNo =\n        recentOrders.invoiceNumber ||\n        this.invoiceNumber ||\n        this.table.invoiceNo;\n\n      const updatedFields = {\n        invoice_id: invoiceNo,\n        reason: this.cancelReason,\n      };\n      this.call\n        .post(\"ury.ury.doctype.ury_order.ury_order.cancel_order\", updatedFields)\n        .then(() => {\n          this.notification.createNotification(\"Invoice Cancelled\");\n          router.push(\"/Table\").then(() => {\n            window.location.reload();\n          });\n        })\n        .catch((error) => console.error(error));\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/posClosing.js",
    "content": "import { defineStore } from \"pinia\";\nimport router from '../router';\nimport { useAlert } from \"./Alert.js\";\nimport { useInvoiceDataStore } from \"./invoiceData.js\";\nimport frappe from \"./frappeSdk.js\";\n\nexport const posClosing = defineStore(\"posClose\", {\n  state: () => ({\n    invoiceData: useInvoiceDataStore(),\n    call: frappe.call(),\n    db: frappe.db(),\n    startDate: null,\n    alert:useAlert(),\n    postingDate: null,\n    periodEndDate: new Date(),\n    posClosecreation: true,\n    posOpenEntries: [],\n    showPosOpen: false,\n    selectedPosOpenEntry: null,\n    cashier: null,\n    postingTime: new Date(),\n    openingBalance: [],\n    closingAmount: 0,\n    formattedDateTime: null,\n    invoiceDetails: [],\n    posInvoice: [],\n    invoiceDate: null,\n    amount: null,\n    payments: [],\n    grandTotal: 0,\n    netTotal: 0,\n    totalQty: 0,\n    totalInvoices: 0,\n    taxes: [],\n    posClosingEntry: null,\n    posClosing: true,\n    posCloseSaved: false,\n    isPosClose: null,\n    showSumbitPosclose: false,\n  }),\n  getters: {\n    isFlagSet() {\n      return this.customer.length === 0;\n    },\n  },\n  actions: {\n    selectPosOpen() {\n      this.db\n        .getDocList(\"POS Opening Entry\", {\n          fields: [\"name\", \"status\", \"branch\", \"docstatus\"],\n          filters: [\n            [\"status\", \"=\", \"Open\"],\n            [\"docstatus\", \"=\", \"1\"],\n          ],\n        })\n        .then((docs) => {\n          this.posOpenEntries = docs;\n        })\n        .catch((error) => console.error(error));\n\n      this.showPosOpen = true;\n    },\n    selectPos(posOpen) {\n      let posOpenEntrieName = null;\n      this.selectedPosOpenEntry = posOpen.name;\n      this.showPosOpen = false;\n      posOpenEntrieName = posOpen.name;\n      const getPosOpenEntry = {\n        doctype: \"POS Opening Entry\",\n        name: posOpenEntrieName,\n      };\n      this.call\n        .get(\"frappe.client.get\", getPosOpenEntry)\n        .then((result) => {\n          this.startDate = result.message.period_start_date;\n          this.cashier = result.message.owner;\n          this.openingBalance = result.message.balance_details;\n\n          this.getInvoice();\n        })\n        .catch((error) => console.error(error));\n    },\n    getInvoice() {\n      if (this.periodEndDate) {\n        const date = new Date(this.periodEndDate);\n        const year = date.getFullYear();\n        const month = String(date.getMonth() + 1).padStart(2, \"0\");\n        const day = String(date.getDate()).padStart(2, \"0\");\n        const hours = String(date.getHours()).padStart(2, \"0\");\n        const minutes = String(date.getMinutes()).padStart(2, \"0\");\n        const seconds = String(date.getSeconds()).padStart(2, \"0\");\n        this.formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n      } else {\n        this.formattedDateTime = null;\n      }\n      const PosOpenEntry = {\n        start: this.startDate,\n        end: this.formattedDateTime,\n        pos_profile: this.invoiceData.posProfile,\n        user: this.cashier,\n      };\n      this.call\n        .get(\n          \"erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_pos_invoices\",\n          PosOpenEntry\n        )\n        .then((result) => {\n          this.invoiceDetails = result.message;\n          let paymentAggregated = {};\n          this.invoiceDetails.forEach((payment) => {\n            this.grandTotal += parseFloat(payment.grand_total);\n            this.netTotal += parseFloat(payment.net_total);\n            this.totalQty += parseFloat(payment.total_qty);\n            let taxes = payment.taxes;\n            let combinedTaxes = {};\n\n            taxes.forEach((item) => {\n              if (!combinedTaxes[item.account_head]) {\n                combinedTaxes[item.account_head] = {\n                  account_head: item.account_head,\n                  rate: item.rate,\n                  tax_amount: 0,\n                };\n              }\n              combinedTaxes[item.account_head].tax_amount += item.tax_amount;\n            });\n\n            this.taxes = Object.values(combinedTaxes);\n\n            payment.payments.forEach((item) => {\n              if (!paymentAggregated[item.mode_of_payment]) {\n                paymentAggregated[item.mode_of_payment] = {\n                  expected_amount: 0,\n                  mode_of_payment: item.mode_of_payment,\n                };\n              }\n              paymentAggregated[item.mode_of_payment].expected_amount +=\n                item.amount;\n            });\n          });\n          this.payments = Object.values(paymentAggregated);\n\n          this.posInvoice = this.invoiceDetails.map((item) => ({\n            pos_invoice: item.name,\n            date: item.modified.split(\" \")[0],\n            amount: item.grand_total,\n          }));\n        })\n        .catch((error) => console.error(error));\n    },\n    savePosClosing() {\n      let formattedTime;\n      if (this.postingTime) {\n        const date = new Date(this.postingTime);\n        const hours = String(date.getHours()).padStart(2, \"0\");\n        const minutes = String(date.getMinutes()).padStart(2, \"0\");\n        const seconds = String(date.getSeconds()).padStart(2, \"0\");\n        formattedTime = `${hours}:${minutes}:${seconds}`;\n      } else {\n        formattedTime = null;\n      }\n      let payment_reconciliation = this.openingBalance;\n      payment_reconciliation.forEach((item) => {\n        let found = false;\n        this.payments.forEach((secondItem) => {\n          if (secondItem.mode_of_payment === item.mode_of_payment) {\n            item.expected_amount = secondItem.expected_amount;\n            item.difference = -secondItem.expected_amount;\n\n            found = true;\n          }\n        });\n\n        if (!found) {\n          item.expected_amount = 0;\n          item.difference = 0;\n        }\n      });\n\n      this.db\n        .createDoc(\"POS Closing Entry\", {\n          period_start_date: this.startDate,\n          period_end_date: this.formattedDateTime,\n          posting_date: this.postingDate,\n          posting_time: formattedTime,\n          company: this.invoiceData.company,\n          pos_profile: this.invoiceData.posProfile,\n          payment_reconciliation: payment_reconciliation,\n          pos_transactions: this.posInvoice,\n          pos_opening_entry: this.selectedPosOpenEntry,\n          user: this.cashier,\n          grand_total: this.grandTotal,\n          net_total: this.netTotal,\n          total_quantity: this.totalQty,\n          docstatus: 0,\n        })\n        .then((doc) => {\n          this.posClosingEntry = doc.name;\n          this.posClosing = false;\n          this.posCloseSaved = true;\n          this.isPosClose = \"Draft\";\n        })\n        .catch((error) => {\n          if (error._server_messages) {\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n            this.alert.createAlert(\"Message\",message.message, \"OK\")\n          }\n        });\n    },\n    getBadgeType() {\n      if (this.isPosClose === \"Draft\") {\n        return \"red\";\n      } else if (this.isPosClose === \"Submitted\") {\n        return \"default\";\n      }\n    },\n    getBadgeText() {\n      if (this.isPosClose == \"Draft\") {\n        return \"Draft\";\n      } else if (this.isPosClose == \"Submitted\") {\n        return \"Submitted\";\n      }\n    },\n    showSumbitPosCloseModal() {\n      this.showSumbitPosclose = true;\n    },\n    sumbitPosClosing() {\n      this.showSumbitPosclose = false;\n      this.db\n        .updateDoc(\"POS Closing Entry\", this.posClosingEntry, {\n          docstatus: 1,\n        })\n        .then((doc) => {\n          this.isPosClose = \"Submitted\";\n        })\n        .catch((error) => {\n          if (error._server_messages) {\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n            this.alert.createAlert(\"Message\",message.message, \"OK\")\n            \n          }\n        });\n    },\n    setFormattedDate() {\n      const currentDate = new Date(); // Get the current date\n      const year = currentDate.getFullYear();\n      const month = String(currentDate.getMonth() + 1).padStart(2, \"0\");\n      const day = String(currentDate.getDate()).padStart(2, \"0\");\n      this.postingDate = `${year}-${month}-${day}`; // Format the date as '2023-08-24'\n    },\n    deleteRow(index) {\n      this.openingBalance.splice(index, 1);\n    },\n    routeToPosClose() {\n      router.push(\"/PosClose\");\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/posOpening.js",
    "content": "import { defineStore } from \"pinia\";\nimport router from \"../router\";\nimport { useInvoiceDataStore } from \"./invoiceData.js\";\nimport { useAlert } from \"./Alert.js\";\nimport frappe from \"./frappeSdk.js\";\n\nexport const posOpening = defineStore(\"posOpen\", {\n  state: () => ({\n    invoiceData: useInvoiceDataStore(),\n    call: frappe.call(),\n    startDate: new Date(),\n    formattedDateTime: null,\n    postingDate: null,\n    alert: useAlert(),\n    posOpencreation: true,\n    currentDate: new Date(),\n    posOpenSaved: false,\n    posOpenEntryName: null,\n    db: frappe.db(),\n    showSumbitPosOpen: false,\n    isPosOpen: null,\n  }),\n  getters: {\n    currentDateTime: {\n      get() {},\n    },\n  },\n  actions: {\n    savePosOpening() {\n      if (this.startDate) {\n        const date = new Date(this.startDate);\n        const year = date.getFullYear();\n        const month = String(date.getMonth() + 1).padStart(2, \"0\");\n        const day = String(date.getDate()).padStart(2, \"0\");\n        const hours = String(date.getHours()).padStart(2, \"0\");\n        const minutes = String(date.getMinutes()).padStart(2, \"0\");\n        const seconds = String(date.getSeconds()).padStart(2, \"0\");\n        this.formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n      } else {\n        this.formattedDateTime = null;\n      }\n\n      this.db\n        .createDoc(\"POS Opening Entry\", {\n          period_start_date: this.formattedDateTime,\n          posting_date: this.postingDate,\n          company: this.invoiceData.company,\n          pos_profile: this.invoiceData.posProfile,\n          balance_details: this.invoiceData.modeOfPaymentList,\n          branch: this.invoiceData.branch,\n          user: this.invoiceData.cashier,\n          docstatus: 0,\n        })\n        .then((doc) => {\n          this.posOpenEntryName = doc.name;\n          this.posOpencreation = false;\n          this.posOpenSaved = true;\n          this.isPosOpen = \"Draft\";\n        })\n        .catch((error) => {\n          if (error._server_messages) {\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n            this.alert.createAlert(\"Message\", message.message, \"OK\");\n          }\n        });\n    },\n    getBadgeType() {\n      if (this.isPosOpen == \"Draft\") {\n        return \"red\";\n      } else if (this.isPosOpen == \"Open\") {\n        return \"yellow\";\n      }\n    },\n    getBadgeText() {\n      if (this.isPosOpen == \"Draft\") {\n        return \"Draft\";\n      } else if (this.isPosOpen == \"Open\") {\n        return \"Open\";\n      }\n    },\n\n    showSumbitPosOpenModal() {\n      this.showSumbitPosOpen = true;\n    },\n    sumbitPosOpening() {\n      this.showSumbitPosOpen = false;\n      this.db\n        .updateDoc(\"POS Opening Entry\", this.posOpenEntryName, {\n          docstatus: 1,\n        })\n        .then((doc) => {\n          this.isPosOpen = \"Open\";\n        })\n        .catch((error) => console.error(error));\n    },\n    setFormattedDate() {\n      const year = this.currentDate.getFullYear();\n      const month = String(this.currentDate.getMonth() + 1).padStart(2, \"0\");\n      const day = String(this.currentDate.getDate()).padStart(2, \"0\");\n      this.postingDate = `${year}-${month}-${day}`;\n    },\n    deleteRow(index) {\n      this.invoiceData.modeOfPaymentList.splice(index, 1);\n    },\n    routeToPosOpen() {\n      router.push(\"/posOpen\");\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/recentOrder.js",
    "content": "import { defineStore } from \"pinia\";\nimport router from \"../router\";\nimport moment from \"moment\";\nimport { useMenuStore } from \"./Menu.js\";\nimport { useCustomerStore } from \"./Customer.js\";\nimport { useNotifications } from \"./Notification.js\";\nimport { useInvoiceDataStore } from \"./invoiceData.js\";\nimport { useTableStore } from \"./Table.js\";\nimport { useAlert } from \"./Alert.js\";\nimport frappe from \"./frappeSdk.js\";\n\nexport const usetoggleRecentOrder = defineStore(\"recentOrders\", {\n  state: () => ({\n    payments: [],\n    pastOrder: [],\n    texDetails: [],\n    pastOrderdItem: [],\n    recentOrderList: [],\n    modeOfPaymentList: [],\n    recentOrderListItems: [],\n    next: false,\n    netTotal: 0,\n    paidAmount: 0,\n    grandTotal: 0,\n    billAmount: 0,\n    currentPage: 1,\n    paymentMethod: 0,\n    editPrintedInvoice: 0,\n    selectedStatus: \"Draft\",\n    posProfile: \"\",\n    searchOrder: \"\",\n    mobileNumber: \"\",\n    customerNameForBilling: \"\",\n    previousOrderdCustomer: \"\",\n    table: null,\n    timer: null,\n    orderType: null,\n    percentage: null,\n    searchTimer: null,\n    postingDate: null,\n    modifiedTime: null,\n    recentWaiter: null,\n    draftInvoice: null,\n    cancelReason: null,\n    pastOrderType: null,\n    selectedTable: null,\n    invoiceNumber: null,\n    selectedOrder: null,\n    totalPercentage: null,\n    discount_amount: null,\n    invoicePrinted: null,\n    restaurantTable: null,\n    modeOfPaymentName: null,\n    additionalPiscountPercentage: null,\n    isLoading: false,\n    isChecked: false,\n    showOrder: false,\n    showInput: false,\n    showDialog: false,\n    showPayment: false,\n    showDiscount: false,\n    cancelInvoiceFlag: false,\n    alert: useAlert(),\n    call: frappe.call(),\n    menu: useMenuStore(),\n    tables: useTableStore(),\n    customers: useCustomerStore(),\n    notification: useNotifications(),\n    invoiceData: useInvoiceDataStore(),\n  }),\n  getters: {\n    filteredOrders() {\n      return this.recentOrderList.filter((order) =>\n        this.matchesSearchOrder(order)\n      );\n    },\n    total() {\n      return this.modeOfPaymentList.reduce(\n        (acc, method) => acc + (method.value || 0),\n        0\n      );\n    },\n    change() {\n      return this.billAmount - this.total;\n    },\n    changeAmount() {\n      const totalPaid = this.payments.reduce((sum, payment) => sum + payment.amount, 0);\n      return Math.max(0, totalPaid - this.grandTotal);\n    },\n    orderNumber() {\n      if (this.draftInvoice || this.invoiceData.invoiceNumber) {\n        let orderNo = this.draftInvoice || this.invoiceData.invoiceNumber;\n        return orderNo;\n      } else {\n        return \"New\";\n      }\n    },\n    totalAmount() {\n      this.totalPercentage = this.grandTotal - (this.percentage / 100) * this.grandTotal;\n      return this.totalPercentage.toFixed(3);\n    },\n  },\n  actions: {\n    async getPosInvoice(selectedStatus, limit, startLimit) {\n      if(this.invoiceData.multipleCashier){\n        const recentOrder = {\n          status: selectedStatus,\n          limit: limit,\n          limit_start: startLimit,\n          cashier:this.invoiceData.cashier\n        };\n        this.call\n          .get(\"ury.ury_pos.api.getInvoiceForCashier\", recentOrder)\n          .then((result) => {\n            console.log(result.message.data,\"result.message.data\")\n            this.recentOrderList = result.message.data;\n            this.next = result.message.next;\n            return this.recentOrderList, this.next;\n          })\n          .catch((error) => console.error(error));\n      }\n      else{\n      const recentOrder = {\n        status: selectedStatus,\n        limit: limit,\n        limit_start: startLimit,\n      };\n      this.call\n        .get(\"ury.ury_pos.api.getPosInvoice\", recentOrder)\n        .then((result) => {\n          this.recentOrderList = result.message.data;\n          this.next = result.message.next;\n          return this.recentOrderList, this.next;\n        })\n        .catch((error) => console.error(error));\n      }\n    },\n    async handleStatusChange() {\n      this.currentPage = 1;\n      let limit = 0;\n      let startLimit = 0;\n      if (this.selectedStatus === \"Recently Paid\") {\n        limit = this.invoiceData.paidLimit;\n        this.getPosInvoice(this.selectedStatus, limit, startLimit);\n      } else {\n        limit = 10;\n        this.getPosInvoice(this.selectedStatus, limit, startLimit);\n      }\n    },\n    async searchPosInvoice(query) {\n      if (!query) {\n        // If the search query is empty, fetch the original data\n        this.handleStatusChange();\n        return;\n      }\n      const searchParams = {\n        query: query,\n        status:this.selectedStatus\n      };\n      this.call\n        .get(\"ury.ury_pos.api.searchPosInvoice\", searchParams)\n        .then((result) => {\n          this.recentOrderList = result.message.data;\n          this.next = result.message.next;\n          return this.recentOrderList, this.next;\n        })\n        .catch((error) => console.error(error));\n    },\n    handleSearchInput(event) {\n      clearTimeout(this.searchTimer);\n      this.searchTimer=setTimeout(()=>{\n        this.searchPosInvoice(event.target.value);\n      },500);\n    },\n    \n    openPaymentModal() {\n      // Reset payment-related data\n      this.payments = [];\n      this.changeToReturn = 0;\n      this.paidAmount = 0;\n      \n      // Reset values in payment method inputs\n      this.modeOfPaymentList.forEach(method => {\n        method.value = 0;\n      });\n      \n      // Show the payment modal\n      this.showPayment = true;\n    },\n    nextPageClick() {\n      this.currentPage += 1;\n      const limit = 10;\n      const startLimit = (this.currentPage - 1) * limit;\n      this.getPosInvoice(this.selectedStatus, limit, startLimit);\n    },\n    previousPageClick() {\n      this.currentPage -= 1;\n      const limit = 10;\n      const startLimit = (this.currentPage - 1) * limit;\n      this.getPosInvoice(this.selectedStatus, limit, startLimit);\n    },\n    matchesSearchOrder(order) {\n      const query = this.searchOrder.toLowerCase();\n      const name = order.name.toLowerCase();\n      const customer = order.customer.toLowerCase();\n      const mobileNumber = order.mobile_number.toLowerCase();\n\n      return name.includes(query) || customer.includes(query) || mobileNumber.includes(query);\n    },\n    getBadgeType(selectedOrder) {\n      if (\n        selectedOrder.status === \"Paid\" ||\n        selectedOrder.status === \"Consolidated\"\n      ) {\n        return \"green\";\n      } else if (selectedOrder.status === \"Return\") {\n        return \"default\";\n      } else if (selectedOrder.status === \"Draft\") {\n        return \"red\";\n      }\n    },\n    getFormattedTime(postingTime) {\n      const dateTime = moment(postingTime, \"HH:mm:ss.SSSSSS\");\n      const formattedDateTime = dateTime.format(\"h:mma\");\n      return formattedDateTime;\n    },\n\n    async viewRecentOrder(recentOrder) {\n      this.payments = [];\n      this.tables.previousOrderdItem=[]\n      this.additionalPiscountPercentage = null\n      this.discountAmount = null\n      this.percentage = \"\"\n      this.showDiscount = false\n      this.showInput= false\n      if (recentOrder.name === this.invoiceNumber) return;\n      this.orderType = recentOrder.order_type;\n      this.netTotal = recentOrder.net_total;\n      if (this.invoiceData.disableRoundedTotal === 1) {\n        this.grandTotal = recentOrder.grand_total;\n      } else {\n        this.grandTotal = recentOrder.rounded_total;\n      }\n      this.invoiceNumber = recentOrder.name;\n      this.additionalPiscountPercentage = recentOrder.additional_discount_percentage\n      this.discountAmount = recentOrder.discount_amount\n      this.selectedOrder = recentOrder;\n      this.selectedTable = recentOrder.restaurant_table;\n      const dateTimeString = `${recentOrder.posting_date}`;\n      const dateTime = moment(dateTimeString, \"YYYY-MM-DD\");\n      this.postingDate = dateTime.format(\"Do MMMM\");\n      this.invoicePrinted = recentOrder.invoice_printed;\n      const getPosInvoiceItems = {\n        invoice: this.invoiceNumber,\n      };\n      this.call\n        .get(\"ury.ury_pos.api.getPosInvoiceItems\", getPosInvoiceItems)\n        .then((result) => {\n          this.recentOrderListItems = result.message[0];\n          this.texDetails = result.message[1];\n        })\n        .catch((error) => console.error(error));\n      this.showOrder = true;\n    },\n    async editOrder() {\n      this.payments = [];\n      let previousOrderdNumberOfPax = \"\";\n      this.pastOrderdItem = [];\n      this.previousOrderdCustomer = \"\";\n      this.pastOrderType = \"\";\n      this.modifiedTime = \"\";\n      this.customers.newCustomerMobileNo=\"\"\n      let items = this.menu.items;\n      this.draftInvoice = this.invoiceNumber;\n      this.editPrintedInvoice = this.invoicePrinted;\n      items.forEach((item) => {\n        item.qty = \"\";\n      });\n      let cart = this.menu.cart;\n      cart.splice(0, cart.length);\n      const getOrderInvoice = {\n        doctype: \"POS Invoice\",\n        name: this.draftInvoice,\n      };\n      this.call\n        .get(\"frappe.client.get\", getOrderInvoice)\n        .then((result) => {\n          let pastOrder = result.message;\n          this.mobileNumber = pastOrder.mobile_number;\n          this.restaurantTable = pastOrder.restaurant_table;\n          this.pastOrderdItem = pastOrder.items;\n          this.recentWaiter = pastOrder.waiter;\n          this.pastOrderType = pastOrder.order_type;\n          this.modifiedTime = pastOrder.modified;\n          this.menu.comments= pastOrder.custom_comments;\n          if (this.pastOrderType) {\n            this.menu.selectedOrderType = pastOrder.order_type;\n            this.menu.pickOrderType();\n            if (this.pastOrderType === \"Aggregators\") {\n              this.menu.selectedAggregator = pastOrder.customer;\n            }\n          }\n          this.previousOrderdCustomer = pastOrder.customer;\n          previousOrderdNumberOfPax = pastOrder.no_of_pax;\n          router.push(\"/Menu\");\n          if (this.previousOrderdCustomer) {\n            this.customers.search = this.previousOrderdCustomer;\n            this.customers.numberOfPax = previousOrderdNumberOfPax;\n            this.customers.fectchCustomerFavouriteItem();\n          } else {\n            this.customers.search = \"\";\n            this.customers.numberOfPax = \"\";\n            this.customers.customerFavouriteItems = \"\";\n          }\n\n          items.forEach((item) => {\n            const previousItem =\n              this.pastOrderdItem &&\n              this.pastOrderdItem.find(\n                (previousItem) => previousItem.item_code === item.item\n              );\n            if (previousItem && !item.qty) {\n              const itemIndex = cart.findIndex((obj) => obj.item === item.item);\n              const itemIndexExists = itemIndex !== -1;\n              if (!itemIndexExists) {\n                item.qty = previousItem.qty;\n                item.comment = previousItem.comment;\n                cart.push(item);\n              }\n            }\n          });\n          if (this.pastOrderdItem && this.pastOrderdItem.length > 0) {\n            this.pastOrderdItem.forEach((previousItem) => {\n              const existsInMenu = items.some(item => item.item === previousItem.item_code);\n              const existsInCart = cart.some(item => item.item === previousItem.item_code);\n              \n              if (!existsInMenu && !existsInCart) {\n                // Item no longer in menu but was in previous order - add it to cart\n                cart.push({\n                  item: previousItem.item_code,\n                  item_name: previousItem.item_name,\n                  rate: previousItem.rate,\n                  qty: previousItem.qty,\n                  comment: previousItem.comment\n                });\n              }\n            });\n          }\n        })\n        .catch((error) => console.error(error));\n    },\n    showInputBox() {\n      this.showInput = true;\n      this.showDiscount = false;\n    },\n\n    toggleDiscount() {\n      if (this.showInput) {\n        this.showInput = false;\n        this.showDiscount = true;\n      } else {\n        this.showDiscount = false;\n        this.showInput = true;\n      }\n    },\n\n    resetTimer() {\n      if (this.timer) {\n        clearTimeout(this.timer);\n      }\n      this.timer = setTimeout(this.hideInputBox, 1000);\n    },\n    hideInputBox() {\n      this.showInput = false;\n      this.showDiscount = true;\n    },\n    getModeofPayment(customer) {\n      if (customer) {\n        const aggregator = {\n          aggregator: customer,\n        };\n        this.call\n          .get(\"ury.ury_pos.api.getAggregatorMOP\", aggregator)\n          .then((result) => {\n            this.modeOfPaymentList = result.message;\n          })\n          .catch((error) => {\n            if (error._server_messages) {\n              const messages = JSON.parse(error._server_messages);\n              const message = JSON.parse(messages[0]);\n              this.alert.createAlert(\"Message\", message.message, \"OK\");\n            }\n          });\n      }\n    },\n    billing: async function () {\n      this.modeOfPaymentList=[]\n      this.openPaymentModal()\n      const getOrderInvoice = {\n        doctype: \"POS Invoice\",\n        name: this.invoiceNumber,\n      };\n      this.call\n        .get(\"frappe.client.get\", getOrderInvoice)\n        .then((result) => {\n          this.pastOrder = result.message;\n          this.customerNameForBilling = this.pastOrder.customer;\n          this.posProfile = this.pastOrder.pos_profile;\n          let orderType = this.pastOrder.order_type;\n          if (orderType === \"Aggregators\") {\n            this.getModeofPayment(this.pastOrder.customer);\n          } else {\n            this.call\n              .get(\"ury.ury_pos.api.getModeOfPayment\")\n              .then((result) => {\n                this.modeOfPaymentList = result.message;\n              })\n              .catch((error) => {\n                // console.error(error)\n              });\n          }\n\n          this.table = this.pastOrder.restaurant_table;\n          if (this.invoicePrinted === 0) {\n            this.alert.createAlert(\n              \"Alert\",\n              \"Please Print Invoice before Payment\",\n              \"OK\"\n            );\n            this.isLoading = false;\n            this.showPayment = false;\n\n          } else {\n            this.showPayment = true;\n          }\n        })\n        .catch((error) => console.error(error));\n    },\n\n    calculatePaidAmount(paymentMethod) {\n      if (this.totalPercentage) {\n        this.billAmount = this.totalPercentage;\n      } else {\n        this.billAmount = this.grandTotal;\n      }\n      // this.billAmount = this.grandTotal;\n      let balanceAmount = this.billAmount - this.total;\n      if (balanceAmount > 0) {\n        paymentMethod.value = balanceAmount;\n        this.paymentMethod = paymentMethod.value;\n        let existingEntryIndex = this.payments.findIndex(\n          (entry) => entry.mode_of_payment === paymentMethod.mode_of_payment\n        );\n\n        if (this.paymentMethod > 0) {\n          // Only proceed if payment is greater than 0\n          if (existingEntryIndex !== -1) {\n            // Update the existing payment entry\n            this.payments[existingEntryIndex].amount = this.paymentMethod;\n          } else {\n            // Add a new payment entry if not found\n            let paidAmount = {\n              mode_of_payment: paymentMethod.mode_of_payment,\n              amount: this.paymentMethod,\n            };\n            this.payments.push(paidAmount);\n          }\n        }\n      }\n    },\n\n    changePaidAmount(name, value) {\n      this.modeOfPaymentName = name;\n      this.paidAmount = parseFloat(value) || 0;\n\n      let existingEntryIndex = this.payments.findIndex(\n        (entry) => entry.mode_of_payment === this.modeOfPaymentName\n      );\n\n      if (this.paidAmount > 0) {\n        // Only add payment if it's greater than 0\n        if (existingEntryIndex !== -1) {\n          // Update the amount for an existing payment method\n          this.payments[existingEntryIndex].amount = this.paidAmount;\n        } else {\n          // Add a new payment entry if not found\n          let paidAmount = {\n            mode_of_payment: this.modeOfPaymentName,\n            amount: this.paidAmount,\n          };\n          this.payments.push(paidAmount);\n        }\n      } else if (existingEntryIndex !== -1) {\n        // Optionally remove the payment if the amount is 0\n        this.payments.splice(existingEntryIndex, 1);\n      }\n    },\n\n    //Making Payment\n    makePayment: async function () {\n\n      this.isLoading = true;\n      const invoicePayment = {\n        table: this.selectedTable,\n        invoice: this.invoiceNumber,\n        customer: this.customerNameForBilling,\n        owner:this.invoiceData.owner,\n        cashier: this.invoiceData.cashier,\n        payments: this.payments,\n        pos_profile: this.posProfile,\n        additionalDiscount: this.percentage\n      };\n      let pay = this.payments;\n      let amount = pay.reduce((total, obj) => obj.amount + total, 0);\n      let r_total = this.grandTotal;\n      let diff = r_total - amount;\n      if (diff > 5 && !this.percentage) {\n        this.alert.createAlert(\"Message\", \"Round Off Limit Exceeded\", \"OK\");\n        this.isLoading = false;\n      } else {\n        this.call\n          .post(\n            \"ury.ury.doctype.ury_order.ury_order.make_invoice\",\n            invoicePayment\n          )\n          .then(() => {\n            this.notification.createNotification(\"Payment Completed\");\n            this.getPosInvoice(this.selectedStatus, 10, 0);\n            this.clearData();\n          })\n          .catch((error) => {\n            this.isLoading = false;\n            const messages = JSON.parse(error._server_messages);\n            const message = JSON.parse(messages[0]);\n            this.alert.createAlert(\"Message\", message.message, \"OK\");\n          });\n      }\n    },\n    clearData() {\n      this.menu.selectedOrderType = \"\";\n      this.isLoading = false;\n      this.pastOrderType = \"\";\n      this.menu.items.forEach((item) => {\n        item.comment = \"\";\n        item.qty = \"\";\n      });\n      this.selectedStatus = \"Draft\";\n      this.menu.cart = [];\n      this.draftInvoice = \"\";\n      this.customers.selectedOrderType = \"\";\n      this.menu.selectedAggregator = \"\";\n      this.invoiceData.invoiceNumber = \"\";\n      this.customers.customerFavouriteItems = \"\";\n      this.customers.search = \"\";\n      this.invoiceData.tableInvoiceNo = \"\";\n      this.pastOrderType = \"\";\n      this.netTotal = 0;\n      this.paidAmount = 0;\n      this.grandTotal = 0;\n      this.billAmount = 0;\n      this.payments = [];\n      this.showOrder = false;\n      this.customerNameForBilling = \"\";\n      this.invoiceNumber = \"\";\n      this.setBackground = \"\";\n      this.recentOrderListItems = [];\n      this.texDetails = [];\n    },\n    showCancelInvoiceModal() {\n      this.call\n        .get(\"ury.ury.api.button_permission.cancel_check\")\n        .then((result) => {\n          if (result.message === true) {\n            this.cancelInvoiceFlag = true;\n            this.cancelReason = \"\";\n          } else {\n            this.alert.createAlert(\n              \"Message\",\n              \"You don't Have Permission to Cancel \",\n              \"OK\"\n            );\n            this.cancelInvoiceFlag = false;\n            this.cancelReason = \"\";\n          }\n        })\n        .catch((error) => {\n          // console.error(error)\n        });\n    },\n\n    cancelInvoice: async function () {\n      const updatedFields = {\n        invoice_id: this.invoiceNumber,\n        reason: this.cancelReason,\n      };\n      this.call\n        .post(\"ury.ury.doctype.ury_order.ury_order.cancel_order\", updatedFields)\n        .then(() => {\n          this.notification.createNotification(\"Invoice Cancelled\");\n          window.location.reload();\n        })\n        .catch((error) => console.error(error));\n    },\n    toggleRecentOrders() {\n      router.push(\"/recentOrder\");\n    },\n  },\n});\n"
  },
  {
    "path": "urypos/src/stores/utils/PrintWithQz.js",
    "content": "import axios from \"axios\";\nimport qz from \"qz-tray\";  \nimport { privateKey } from \"../../../privateKey\";\n\nimport {\n    KEYUTIL,\n    KJUR,\n    stob64,\n    hextorstr,\n} from 'jsrsasign';\n\nexport function loadQzPrinter(host){\n    return new Promise((resolve,reject)=>{\n        qz.security.setCertificatePromise((resolve)=>{\n            axios.get(\"/assets/ury/files/cert.pem\")\n            .then((response)=>{\n                resolve(response.data)  \n            }).catch((err)=>{\n                reject({\n                    custom:true,\n                    title:\"Error during fetching certificate\",\n                    message:err\n                });\n            })\n        });\n        if(!qz.websocket.isActive()){\n            qz.websocket.connect({\n                host,\n                usingSecure:false\n            })\n            .then(()=>resolve(\"success\"))\n            .catch((err)=>{\n                reject({\n                    custom:true,\n                    title:\"Error during connection to printer\",\n                    message:String(err)\n                });\n            })\n        }\n    });\n}\n\nexport function disconnectQzPrinter(){\n    if(qz.websocket.isActive())\n        qz.websocket.disconnect();\n}\n\nexport function printWithQz(host,htmlToPrint){\n    \n    return new Promise((resolve,reject)=>{\n        qz.security.setSignatureAlgorithm(\"SHA512\"); // Since 2.1\n        qz.security.setSignaturePromise(function(toSign) {\n            return function(resolve) {\n                try {\n                    var pk = KEYUTIL.getKey(privateKey);\n                    var sig = new KJUR.crypto.Signature({\"alg\": \"SHA512withRSA\"});  // Use \"SHA1withRSA\" for QZ Tray 2.0 and older\n                    sig.init(pk); \n                    sig.updateString(toSign);\n                    var hex = sig.sign();\n                    resolve(stob64(hextorstr(hex)));\n                } catch (err) {\n                    reject(err);\n                }\n            };\n        });\n\n        const printing=()=>{\n            qz.printers.getDefault()\n                .then(async (printer)=>{\n                    const data=[{\n                        type:\"html\",\n                        format:\"plain\",\n                        data:htmlToPrint\n                    }];\n                    const config=qz.configs.create(printer)\n                    try {\n                        await qz.print(config, data);\n                        return resolve(\"printed\");\n                    } catch (e) {\n                        qz.websocket.disconnect();\n                        reject(\n                            {\n                                custom: true,\n                                title: \"Print failed\",\n                                message: String(e)\n                            }\n                        );\n                    }\n                })\n                .catch((err)=>{\n                    qz.websocket.disconnect();\n                    reject({\n                        custom:true,\n                        title:\"Error looking up for printer\",\n                        message:String(err)\n                    })\n            })\n        }\n        \n        if(qz.websocket.isActive()){\n            printing();\n        }\n        else{\n            loadQzPrinter(host).then(()=>printing())\n            .catch((err)=>reject(err))\n        }\n    })\n}"
  },
  {
    "path": "urypos/src/style.css",
    "content": ":root {\n  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;\n  line-height: 1.5;\n  font-weight: 400;\n\n  color-scheme: light dark;\n  color: rgba(255, 255, 255, 0.87);\n  background-color: #242424;\n\n  font-synthesis: none;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-text-size-adjust: 100%;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\nbody {\n  margin: 0;\n  display: flex;\n  place-items: center;\n  min-width: 320px;\n  min-height: 100vh;\n}\n\nh1 {\n  font-size: 3.2em;\n  line-height: 1.1;\n}\n\nbutton {\n  border-radius: 8px;\n  border: 1px solid transparent;\n  padding: 0.6em 1.2em;\n  font-size: 1em;\n  font-weight: 500;\n  font-family: inherit;\n  background-color: #1a1a1a;\n  cursor: pointer;\n  transition: border-color 0.25s;\n}\nbutton:hover {\n  border-color: #646cff;\n}\nbutton:focus,\nbutton:focus-visible {\n  outline: 4px auto -webkit-focus-ring-color;\n}\n\n.card {\n  padding: 2em;\n}\n\n#app {\n  max-width: 1280px;\n  margin: 0 auto;\n  padding: 2rem;\n  text-align: center;\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    color: #213547;\n    background-color: #ffffff;\n  }\n  a:hover {\n    color: #747bff;\n  }\n  button {\n    background-color: #f9f9f9;\n  }\n}\n"
  },
  {
    "path": "urypos/src/views/Home.vue",
    "content": "<template>\n  <div>\n    <h1>Home Page</h1>\n    <!-- Fetch the resource on click -->\n    <button @click=\"$resources.ping.fetch()\">Ping</button>\n  </div>\n</template>\n\n<script>\nexport default {\n  resources: {\n    ping() {\n      return {\n        method: \"frappe.ping\", // Method to call on backend\n        onSuccess(d) {\n          alert(d);\n        },\n      };\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/src/views/Login.vue",
    "content": "<template>\n  <div class=\"min-h-screen bg-white flex\">\n    <div class=\"mx-auto w-full max-w-sm lg:w-96\">\n      <form @submit.prevent=\"login\" class=\"space-y-6\">\n        <label for=\"email\"> Username: </label>\n        <input type=\"text\" v-model=\"email\" />\n        <br />\n        <label for=\"password\"> Password: </label>\n        <input type=\"password\" v-model=\"password\" />\n\n        <button\n          class=\"bg-blue-500 block text-white p-2 hover:bg-blue-700\"\n          type=\"submit\"\n        >\n          Sign in\n        </button>\n      </form>\n    </div>\n  </div>\n</template>\n<script>\nexport default {\n  data() {\n    return {\n      email: null,\n      password: null,\n    };\n  },\n  inject: [\"$auth\"],\n  async mounted() {\n    if (this.$route?.query?.route) {\n      this.redirect_route = this.$route.query.route;\n      this.$router.replace({ query: null });\n    }\n  },\n  methods: {\n    async login() {\n      if (this.email && this.password) {\n        let res = await this.$auth.login(this.email, this.password);\n        if (res) {\n          this.$router.push({ name: \"Home\" });\n        }\n      }\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "urypos/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./src/**/*.{html,jsx,tsx,vue,js,ts}\",'node_modules/flowbite-vue/**/*.{js,jsx,ts,tsx}',\n  'node_modules/flowbite/**/*.{js,jsx,ts,tsx}'],\n  theme: {\n    extend: {},\n  },\n  plugins: [ require('flowbite/plugin')],\n}"
  },
  {
    "path": "urypos/vite.config.js",
    "content": "import path from 'path';\nimport { defineConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\nimport proxyOptions from './proxyOptions';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n\tplugins: [vue()],\n\tserver: {\n\t\tport: 8080,\n\t\tproxy: proxyOptions\n\t},\n\tresolve: {\n\t\talias: {\n\t\t\t'@': path.resolve(__dirname, 'src')\n\t\t}\n\t},\n\tbuild: {\n\t\toutDir: '../ury/public/urypos',\n\t\temptyOutDir: true,\n\t\ttarget: 'es2015',\n\t},\n});\n"
  }
]