u
This commit is contained in:
@@ -183,8 +183,10 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
||||
| [`routes/unit/delete.md`](routes/unit/delete.md) | `DELETE /unit` |
|
||||
| [`routes/unit/list.md`](routes/unit/list.md) | `GET /unit` |
|
||||
| [`routes/unit/upsert.md`](routes/unit/upsert.md) | `PUT /unit` |
|
||||
| [`routes/v1/category.md`](routes/v1/category.md) | `GET /v1/category` |
|
||||
| [`routes/pos/client.md`](routes/pos/client.md) | `GET /v1/client` |
|
||||
| [`routes/pos/init.md`](routes/pos/init.md) | `GET /v1/init` |
|
||||
| [`routes/v1/product.md`](routes/v1/product.md) | `GET /v1/product` |
|
||||
| [`routes/warehouse/list.md`](routes/warehouse/list.md) | `GET /warehouse` |
|
||||
| [`routes/warehouse/create.md`](routes/warehouse/create.md) | `POST /warehouse` |
|
||||
| [`routes/warehouse/order.md`](routes/warehouse/order.md) | `PUT /warehouse/order` |
|
||||
|
||||
@@ -13576,6 +13576,57 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/product": {
|
||||
"get": {
|
||||
"summary": "JTL-POS product catalog synchronization",
|
||||
"description": "Returns product catalog with prices, stock, variants, and image hashes modified after lastChangedProduct cursor.",
|
||||
"tags": [
|
||||
"POS Sync"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "lastChangedProduct",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Unix timestamp cursor of last product sync"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "limit",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 50
|
||||
},
|
||||
"description": "Maximum number of products to return"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Array of product objects for JTL-POS",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/init": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -13841,6 +13892,57 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/category": {
|
||||
"get": {
|
||||
"summary": "JTL-POS category synchronization",
|
||||
"description": "Returns category hierarchy modified after lastChangedCategory cursor for the authenticated POS register.",
|
||||
"tags": [
|
||||
"POS Sync"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "lastChangedCategory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Unix timestamp cursor of last category sync"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "limit",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 50
|
||||
},
|
||||
"description": "Maximum number of categories to return"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Array of category objects for JTL-POS",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
34
docs/api/routes/v1/category.md
Normal file
34
docs/api/routes/v1/category.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# `GET /v1/category`
|
||||
|
||||
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||
|
||||
JTL-POS category synchronization
|
||||
|
||||
Returns category hierarchy modified after lastChangedCategory cursor for the authenticated POS register.
|
||||
|
||||
## Query parameters
|
||||
|
||||
| Param | Required | Type | Description |
|
||||
|-------|----------|------|-------------|
|
||||
| `lastChangedCategory` | no | string | Unix timestamp cursor of last category sync |
|
||||
| `limit` | no | integer | Maximum number of categories to return |
|
||||
|
||||
## Response `200`
|
||||
|
||||
Array of category objects for JTL-POS
|
||||
|
||||
```json
|
||||
[
|
||||
{}
|
||||
]
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| 401 | Unauthorized |
|
||||
|
||||
---
|
||||
|
||||
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||
34
docs/api/routes/v1/product.md
Normal file
34
docs/api/routes/v1/product.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# `GET /v1/product`
|
||||
|
||||
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||
|
||||
JTL-POS product catalog synchronization
|
||||
|
||||
Returns product catalog with prices, stock, variants, and image hashes modified after lastChangedProduct cursor.
|
||||
|
||||
## Query parameters
|
||||
|
||||
| Param | Required | Type | Description |
|
||||
|-------|----------|------|-------------|
|
||||
| `lastChangedProduct` | no | string | Unix timestamp cursor of last product sync |
|
||||
| `limit` | no | integer | Maximum number of products to return |
|
||||
|
||||
## Response `200`
|
||||
|
||||
Array of product objects for JTL-POS
|
||||
|
||||
```json
|
||||
[
|
||||
{}
|
||||
]
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| 401 | Unauthorized |
|
||||
|
||||
---
|
||||
|
||||
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||
@@ -38,6 +38,13 @@ function emitStockEvents(companyId, body, result) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(result?.lines)) {
|
||||
for (const line of result.lines) {
|
||||
if (line?.sku) {
|
||||
emit(`article-${line.sku}`, { companyId });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,3 +85,13 @@ export async function getMaxOrderIdCount(params) {
|
||||
return store.posterminal.getMaxOrderIdCount(params);
|
||||
}
|
||||
|
||||
export async function getCategoryList(params) {
|
||||
const store = await getStore();
|
||||
return store.posterminal.getCategoryList(params);
|
||||
}
|
||||
|
||||
export async function getProductList(params) {
|
||||
const store = await getStore();
|
||||
return store.posterminal.getProductList(params);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Router } from 'express';
|
||||
import { posAuthMiddleware } from './middleware/posAuth.js';
|
||||
import { handleClient } from './routes/client.js';
|
||||
import { handleInit } from './routes/init.js';
|
||||
import { handleCategory } from './routes/category.js';
|
||||
import { handleProduct } from './routes/product.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -11,5 +13,9 @@ router.get('/client', handleClient);
|
||||
// POS sync initialization counts
|
||||
router.get('/init', posAuthMiddleware, handleInit);
|
||||
|
||||
// POS master data synchronization
|
||||
router.get('/category', posAuthMiddleware, handleCategory);
|
||||
router.get('/product', posAuthMiddleware, handleProduct);
|
||||
|
||||
export default router;
|
||||
|
||||
|
||||
67
src/v1/routes/category.js
Normal file
67
src/v1/routes/category.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { getCategoryList } from '../../db/posterminal.js';
|
||||
|
||||
function formatServerTimestamp() {
|
||||
const d = new Date();
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /v1/category:
|
||||
* get:
|
||||
* summary: JTL-POS category synchronization
|
||||
* description: Returns category hierarchy modified after lastChangedCategory cursor for the authenticated POS register.
|
||||
* tags:
|
||||
* - POS Sync
|
||||
* security:
|
||||
* - BearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: lastChangedCategory
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Unix timestamp cursor of last category sync
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 50
|
||||
* description: Maximum number of categories to return
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Array of category objects for JTL-POS
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
*/
|
||||
export async function handleCategory(req, res, next) {
|
||||
try {
|
||||
const posterminal = req.posterminal;
|
||||
const cursor = Number(req.query.lastChangedCategory) || 0;
|
||||
const limit = Number(req.query.limit) || 20;
|
||||
|
||||
const categories = await getCategoryList({
|
||||
posterminalId: posterminal.id,
|
||||
cursor,
|
||||
limit,
|
||||
});
|
||||
|
||||
const timestamp = formatServerTimestamp();
|
||||
|
||||
res.json(
|
||||
categories.map((category) => ({
|
||||
...category,
|
||||
updated_at: timestamp,
|
||||
created_at: timestamp,
|
||||
}))
|
||||
);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
104
src/v1/routes/product.js
Normal file
104
src/v1/routes/product.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import { getProductList } from '../../db/posterminal.js';
|
||||
|
||||
const STATIC_DEFAULTS = {
|
||||
sort: '0',
|
||||
p_price: '0.00',
|
||||
discountable: '0',
|
||||
deposit: '0',
|
||||
discount: '',
|
||||
d_price: '0.0',
|
||||
tax_rate2: '',
|
||||
use_in_out_tax: '0',
|
||||
barcode: null,
|
||||
use_stock: '0',
|
||||
q_div: '0',
|
||||
quantity: '0',
|
||||
unit: null,
|
||||
single_bookable: '0',
|
||||
annotation: '',
|
||||
status: '0',
|
||||
tags: '',
|
||||
is_parent: '0',
|
||||
parent: '0',
|
||||
variants: '',
|
||||
print_kitchen_receipt: '0',
|
||||
deposit_name: '',
|
||||
updated_at: '0001-01-01 00:00:00',
|
||||
isCompositeProduct: '0',
|
||||
attributes: [],
|
||||
configurationGroups: '',
|
||||
options: null,
|
||||
hasBestBeforeDate: '0',
|
||||
hasLotNumber: '0',
|
||||
hasSerialNumber: '0',
|
||||
PLU: '',
|
||||
short_description: '',
|
||||
minStock: '0',
|
||||
container: [],
|
||||
reservedQuantity: '0.00',
|
||||
deliveryDetails: [],
|
||||
isbn: '',
|
||||
manufacturerName: null,
|
||||
han: null,
|
||||
productType: '0',
|
||||
voucherData: null,
|
||||
inputPrice: '0',
|
||||
inputQuantity: '0',
|
||||
};
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /v1/product:
|
||||
* get:
|
||||
* summary: JTL-POS product catalog synchronization
|
||||
* description: Returns product catalog with prices, stock, variants, and image hashes modified after lastChangedProduct cursor.
|
||||
* tags:
|
||||
* - POS Sync
|
||||
* security:
|
||||
* - BearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: lastChangedProduct
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Unix timestamp cursor of last product sync
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 50
|
||||
* description: Maximum number of products to return
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Array of product objects for JTL-POS
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
*/
|
||||
export async function handleProduct(req, res, next) {
|
||||
try {
|
||||
const posterminal = req.posterminal;
|
||||
const cursor = Number(req.query.lastChangedProduct) || 0;
|
||||
const limit = Number(req.query.limit) || 20;
|
||||
|
||||
const products = await getProductList({
|
||||
posterminalId: posterminal.id,
|
||||
cursor,
|
||||
limit,
|
||||
});
|
||||
|
||||
res.json(
|
||||
products.map((product) => ({
|
||||
...STATIC_DEFAULTS,
|
||||
...product,
|
||||
}))
|
||||
);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user