openapi: 3.0.3 info: title: 'Laravel NWMS API' description: 'NWMS API documentation.' version: 1.0.0 servers: - url: 'https://nwms.cloud' tags: - name: Acceptances description: "\nGoods receiving documents (warehouse inbound).\nUse these endpoints to list, create, view, update and delete acceptances.\n\n**Authentication:** Bearer Token (Laravel Sanctum)\n\n**Access scope**\n- Results are automatically scoped to the caller’s `acc_domain_id` (their tenant/domain)." - name: 'Acceptances » Lines' description: "\nManage line items (offers) inside an acceptance (warehouse inbound document).\n\n**Authentication:** Bearer Token (Laravel Sanctum)\n\n**Access scope**\n- All operations are restricted to the caller’s domain (`acc_domain_id = user.domain_id`).\n- The acceptance must exist and belong to the caller’s domain." - name: Orders description: "\nManage warehouse orders and their associated offers.\n\nEach order represents a customer shipment or acceptance document,\ndepending on the order type. Orders can include delivery information,\ncontact details, and multiple product offers.\n\n**Authentication:** Bearer Token via Sanctum" - name: Products description: "\nEndpoints for managing the product catalog (\"offers\").\n\n### Authorization\nAll endpoints require **Bearer Token** (Laravel Sanctum) unless explicitly marked `@unauthenticated`.\n\n### Domain & Access Scoping\n- Non-admin users are **automatically scoped** to their `of_domain_id` (the same as the user's `domain_id`).\n- Additionally, if a user is **not** a `warehouse_manager`, they can only access products belonging to shops where\n `sh_user_id` ∈ { current_user.id, current_user.parent_id } (within the same domain).\n\n### Server-calculated Fields\nFields like **`rests`** (current stock balances) are computed by the server and **must not** be included in request payloads.\nThey are returned in responses (see `OfferResource`)." - name: Users description: '' components: securitySchemes: default: type: http scheme: bearer description: null security: - default: [] paths: /api/acceptances: get: summary: 'List acceptances' operationId: listAcceptances description: "Returns a paginated list of acceptances for the authenticated user’s domain.\nIncludes linked relations for convenience: status, type, warehouse, creator and offers." parameters: - in: query name: page description: 'The page number for pagination.' example: 1 required: false schema: type: integer description: 'The page number for pagination.' example: 1 nullable: false - in: query name: per_page description: 'Items per page (max 250).' example: 20 required: false schema: type: integer description: 'Items per page (max 250).' example: 20 nullable: false - in: query name: acc_wh_id description: 'Filter by warehouse ID.' example: 3 required: false schema: type: integer description: 'Filter by warehouse ID.' example: 3 nullable: false - in: query name: acc_shop_id description: 'Filter by shop ID.' example: 12 required: false schema: type: integer description: 'Filter by shop ID.' example: 12 nullable: false - in: query name: acc_type description: 'Filter by acceptance type ID.' example: 1 required: false schema: type: integer description: 'Filter by acceptance type ID.' example: 1 nullable: false responses: 200: description: 'Success (paginated)' content: application/json: schema: type: object example: data: - acc_id: 101 acc_wh_id: 3 acc_shop_id: 12 acc_type: 1 acc_status: 1 acc_comment: 'Inbound from supplier ACME' acc_date: '2025-11-12T08:30:00Z' getAccStatus: las_id: 1 las_name: Created getAccType: lat_id: 1 lat_name: 'Supplier receipt' getWarehouse: wh_id: 3 wh_name: 'Main DC' getUser: id: 5 name: 'Operator John' offers: - aof_id: 555 aof_offer_id: 77 aof_qty: 10 aof_price: 4.99 links: first: 'https://api.example.com/api/acceptances?page=1' last: 'https://api.example.com/api/acceptances?page=5' prev: null next: 'https://api.example.com/api/acceptances?page=2' meta: current_page: 1 from: 1 last_page: 5 path: 'https://api.example.com/api/acceptances' per_page: 20 to: 20 total: 100 properties: data: type: array example: - acc_id: 101 acc_wh_id: 3 acc_shop_id: 12 acc_type: 1 acc_status: 1 acc_comment: 'Inbound from supplier ACME' acc_date: '2025-11-12T08:30:00Z' getAccStatus: las_id: 1 las_name: Created getAccType: lat_id: 1 lat_name: 'Supplier receipt' getWarehouse: wh_id: 3 wh_name: 'Main DC' getUser: id: 5 name: 'Operator John' offers: - aof_id: 555 aof_offer_id: 77 aof_qty: 10 aof_price: 4.99 items: type: object properties: acc_id: type: integer example: 101 acc_wh_id: type: integer example: 3 acc_shop_id: type: integer example: 12 acc_type: type: integer example: 1 acc_status: type: integer example: 1 acc_comment: type: string example: 'Inbound from supplier ACME' acc_date: type: string example: '2025-11-12T08:30:00Z' getAccStatus: type: object properties: las_id: type: integer example: 1 las_name: type: string example: Created getAccType: type: object properties: lat_id: type: integer example: 1 lat_name: type: string example: 'Supplier receipt' getWarehouse: type: object properties: wh_id: type: integer example: 3 wh_name: type: string example: 'Main DC' getUser: type: object properties: id: type: integer example: 5 name: type: string example: 'Operator John' offers: type: array example: - aof_id: 555 aof_offer_id: 77 aof_qty: 10 aof_price: 4.99 items: type: object properties: aof_id: type: integer example: 555 aof_offer_id: type: integer example: 77 aof_qty: type: integer example: 10 aof_price: type: number example: 4.99 links: type: object properties: first: type: string example: 'https://api.example.com/api/acceptances?page=1' last: type: string example: 'https://api.example.com/api/acceptances?page=5' prev: type: string example: null next: type: string example: 'https://api.example.com/api/acceptances?page=2' meta: type: object properties: current_page: type: integer example: 1 from: type: integer example: 1 last_page: type: integer example: 5 path: type: string example: 'https://api.example.com/api/acceptances' per_page: type: integer example: 20 to: type: integer example: 20 total: type: integer example: 100 tags: - Acceptances post: summary: 'Create acceptance' operationId: createAcceptance description: "Creates a new acceptance (warehouse inbound) in the caller’s domain.\nServer sets initial status to **1** (created) and the `acc_date` to now." parameters: [] responses: 201: description: Created content: application/json: schema: type: object example: acc_id: 101 acc_wh_id: 3 acc_shop_id: 12 acc_type: 1 acc_status: 1 acc_comment: 'Inbound from ACME' acc_date: '2025-11-12T08:30:00Z' properties: acc_id: type: integer example: 101 acc_wh_id: type: integer example: 3 acc_shop_id: type: integer example: 12 acc_type: type: integer example: 1 acc_status: type: integer example: 1 acc_comment: type: string example: 'Inbound from ACME' acc_date: type: string example: '2025-11-12T08:30:00Z' 422: description: 'Validation error' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: acc_wh_id: - 'The acc_wh_id field is required.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: acc_wh_id: type: array example: - 'The acc_wh_id field is required.' items: type: string tags: - Acceptances requestBody: required: true content: application/json: schema: type: object properties: acc_wh_id: type: integer description: 'Warehouse ID where goods are received.' example: 3 nullable: false acc_shop_id: type: integer description: 'Shop (merchant) ID this document belongs to.' example: 12 nullable: false acc_type: type: integer description: 'Acceptance type ID (eg. supplier receipt).' example: 1 nullable: false acc_comment: type: string description: 'A short operator comment (max 255 chars).' example: '"Inbound from ACME"' nullable: true required: - acc_wh_id - acc_shop_id - acc_type '/api/acceptances/{id}': get: summary: 'Get acceptance by ID' operationId: getAcceptanceByID description: 'Returns a single acceptance with related entities and line items.' parameters: [] responses: 200: description: Found content: application/json: schema: type: object example: acc_id: 101 acc_wh_id: 3 acc_shop_id: 12 acc_type: 1 acc_status: 1 acc_comment: 'Inbound from ACME' acc_date: '2025-11-12T08:30:00Z' offers: - aof_id: 555 aof_offer_id: 77 aof_qty: 10 aof_price: 4.99 properties: acc_id: type: integer example: 101 acc_wh_id: type: integer example: 3 acc_shop_id: type: integer example: 12 acc_type: type: integer example: 1 acc_status: type: integer example: 1 acc_comment: type: string example: 'Inbound from ACME' acc_date: type: string example: '2025-11-12T08:30:00Z' offers: type: array example: - aof_id: 555 aof_offer_id: 77 aof_qty: 10 aof_price: 4.99 items: type: object properties: aof_id: type: integer example: 555 aof_offer_id: type: integer example: 77 aof_qty: type: integer example: 10 aof_price: type: number example: 4.99 404: description: '' content: application/json: schema: type: object example: message: 'Not found' properties: message: type: string example: 'Not found' tags: - Acceptances put: summary: 'Update acceptance' operationId: updateAcceptance description: "Updates editable fields of an existing acceptance in the caller’s domain.\nTypically, status transitions are handled elsewhere; here we focus on basic fields." parameters: [] responses: 200: description: Updated content: application/json: schema: type: object example: acc_id: 101 acc_comment: 'Adjusted note' properties: acc_id: type: integer example: 101 acc_comment: type: string example: 'Adjusted note' 404: description: '' content: application/json: schema: type: object example: message: 'Not found' properties: message: type: string example: 'Not found' 422: description: 'Validation error' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: acc_comment: - 'The acc_comment may not be greater than 255 characters.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: acc_comment: type: array example: - 'The acc_comment may not be greater than 255 characters.' items: type: string tags: - Acceptances requestBody: required: false content: application/json: schema: type: object properties: acc_wh_id: type: integer description: 'Warehouse ID.' example: 4 nullable: false acc_shop_id: type: integer description: 'Shop ID.' example: 12 nullable: false acc_type: type: integer description: 'Acceptance type ID.' example: 2 nullable: false acc_comment: type: string description: 'Comment (max 255 chars).' example: '"Adjusted note"' nullable: false delete: summary: 'Delete acceptance' operationId: deleteAcceptance description: "Deletes an acceptance in the caller’s domain.\nBe careful: associated offers/stock postings may require additional handling by business logic." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: Deleted properties: message: type: string example: Deleted 404: description: '' content: application/json: schema: type: object example: message: 'Not found' properties: message: type: string example: 'Not found' tags: - Acceptances parameters: - in: path name: id description: 'Acceptance ID.' example: 101 required: true schema: type: integer '/api/acceptances/{id}/offers': post: summary: 'Add offers (lines) to an acceptance' operationId: addOfferslinesToAnAcceptance description: "Appends one or more offer lines to an existing acceptance.\nEach line contains the product (offer) ID and the expected quantity to receive.\n\nNotes:\n- The server will link each line to the specified acceptance.\n- Do not supply server-calculated fields; only editable inputs are accepted." parameters: [] responses: 201: description: Created content: application/json: schema: type: array items: type: object properties: ao_id: type: integer example: 555 ao_acceptance_id: type: integer example: 101 ao_offer_id: type: integer example: 77 ao_expected: type: integer example: 10 ao_price: type: number example: 4.99 ao_batch: type: string example: LOT-2025-11 ao_expiration_date: type: string example: '2026-03-31' ao_barcode: type: string example: EAN-1234567890123 example: - ao_id: 555 ao_acceptance_id: 101 ao_offer_id: 77 ao_expected: 10 ao_price: 4.99 ao_batch: LOT-2025-11 ao_expiration_date: '2026-03-31' ao_barcode: EAN-1234567890123 404: description: 'Acceptance not found / out of scope' content: application/json: schema: type: object example: message: 'No query results for model' properties: message: type: string example: 'No query results for model' 422: description: 'Validation error' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: 0.ao_offer_id: - 'The 0.ao_offer_id field is required.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: 0.ao_offer_id: type: array example: - 'The 0.ao_offer_id field is required.' items: type: string tags: - 'Acceptances » Lines' requestBody: required: true content: application/json: schema: type: object properties: '*': type: array description: 'Array of line objects (no top-level key, raw JSON array).' example: - ao_offer_id: 77 ao_expected: 10 ao_price: 4.99 items: type: string required: - '*' parameters: - in: path name: id description: 'The acceptance ID you are adding lines to.' example: 101 required: true schema: type: integer '/api/acceptances/{acceptance_id}/offers/{offer_id}': put: summary: 'Update a line in an acceptance' operationId: updateALineInAnAcceptance description: 'Updates a single acceptance line (quantity, price, batch details, etc.).' parameters: [] responses: 200: description: Updated content: application/json: schema: type: object example: ao_id: 555 ao_acceptance_id: 101 ao_offer_id: 77 ao_expected: 12.5 ao_price: 5.25 ao_batch: LOT-2025-12 ao_expiration_date: '2026-05-15' ao_barcode: EAN-9876543210987 properties: ao_id: type: integer example: 555 ao_acceptance_id: type: integer example: 101 ao_offer_id: type: integer example: 77 ao_expected: type: number example: 12.5 ao_price: type: number example: 5.25 ao_batch: type: string example: LOT-2025-12 ao_expiration_date: type: string example: '2026-05-15' ao_barcode: type: string example: EAN-9876543210987 404: description: 'Acceptance or line not found / out of scope' content: application/json: schema: type: object example: message: 'No query results for model' properties: message: type: string example: 'No query results for model' 422: description: 'Validation error' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: ao_expected: - 'The ao_expected must be at least 0.01.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: ao_expected: type: array example: - 'The ao_expected must be at least 0.01.' items: type: string tags: - 'Acceptances » Lines' requestBody: required: false content: application/json: schema: type: object properties: ao_expected: type: number description: 'Updated expected quantity (≥ 0.01).' example: 12.5 nullable: false ao_price: type: number description: 'Updated price per unit.' example: 5.25 nullable: true ao_batch: type: string description: 'Batch/Lot identifier (max 50).' example: '"LOT-2025-12"' nullable: true ao_expiration_date: type: date description: 'Expiration date (YYYY-MM-DD).' example: '2026-05-15' nullable: true ao_barcode: type: string description: 'External barcode/label (max 100).' example: '"EAN-9876543210987"' nullable: true delete: summary: 'Delete a line from an acceptance' operationId: deleteALineFromAnAcceptance description: "Removes a single line from an acceptance document.\nMake sure there are no dependent stock movements you still need." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: Deleted properties: message: type: string example: Deleted 404: description: 'Acceptance or line not found / out of scope' content: application/json: schema: type: object example: message: 'No query results for model' properties: message: type: string example: 'No query results for model' tags: - 'Acceptances » Lines' parameters: - in: path name: acceptance_id description: 'The ID of the acceptance.' example: architecto required: true schema: type: string - in: path name: offer_id description: 'The ID of the offer.' example: architecto required: true schema: type: string - in: path name: acc_id description: 'Acceptance ID.' example: 101 required: true schema: type: integer - in: path name: ao_id description: 'Line (acceptance offer) ID.' example: 555 required: true schema: type: integer /api/orders: get: summary: 'Get list of orders' operationId: getListOfOrders description: "Returns a paginated list of all orders available to the authenticated user.\nAccess depends on user roles: admins see all, warehouse managers see their domain,\nshop users see only their shop’s orders." parameters: - in: query name: per_page description: 'optional Number of records per page.' example: 50 required: false schema: type: integer description: 'optional Number of records per page.' example: 50 nullable: false - in: query name: page description: 'optional Page number for pagination.' example: 1 required: false schema: type: integer description: 'optional Page number for pagination.' example: 1 nullable: false - in: query name: o_ext_id description: 'optional External order ID for filtering.' example: ORD-12345 required: false schema: type: string description: 'optional External order ID for filtering.' example: ORD-12345 nullable: false responses: 200: description: Successful content: application/json: schema: type: object example: data: - o_id: 105 o_ext_id: ORD-105 o_shop_id: 1 o_wh_id: 2 o_status_id: 10 o_sum: 199.8 created_at: '2025-11-12T08:30:00Z' meta: current_page: 1 per_page: 50 total: 312 properties: data: type: array example: - o_id: 105 o_ext_id: ORD-105 o_shop_id: 1 o_wh_id: 2 o_status_id: 10 o_sum: 199.8 created_at: '2025-11-12T08:30:00Z' items: type: object properties: o_id: type: integer example: 105 o_ext_id: type: string example: ORD-105 o_shop_id: type: integer example: 1 o_wh_id: type: integer example: 2 o_status_id: type: integer example: 10 o_sum: type: number example: 199.8 created_at: type: string example: '2025-11-12T08:30:00Z' meta: type: object properties: current_page: type: integer example: 1 per_page: type: integer example: 50 total: type: integer example: 312 tags: - Orders post: summary: 'Create a new order' operationId: createANewOrder description: "Creates a new order with optional contact and delivery details.\nSome fields (like total sums) are calculated automatically and\nshould not be included in the request." parameters: [] responses: 201: description: Created content: application/json: schema: type: object example: o_id: 105 o_ext_id: ORD-105 o_sum: 0 contact: oc_first_name: John ds: ods_track_number: TRACK-12345 properties: o_id: type: integer example: 105 o_ext_id: type: string example: ORD-105 o_sum: type: integer example: 0 contact: type: object properties: oc_first_name: type: string example: John ds: type: object properties: ods_track_number: type: string example: TRACK-12345 422: description: 'Validation error' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: o_type_id: - 'The o_type_id field is required.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: o_type_id: type: array example: - 'The o_type_id field is required.' items: type: string tags: - Orders requestBody: required: true content: application/json: schema: type: object properties: o_type_id: type: integer description: 'Order type ID.' example: 1 nullable: false o_ext_id: type: string description: 'optional External order identifier.' example: ORD-105 nullable: true o_shop_id: type: integer description: 'Shop ID.' example: 1 nullable: false o_wh_id: type: integer description: 'Warehouse ID.' example: 2 nullable: false o_date_send: type: date description: 'optional Planned shipping date.' example: '2025-11-15' nullable: true o_source_id: type: integer description: 'optional Source or sales channel ID.' example: 3 nullable: true o_status_id: type: integer description: '' example: 16 nullable: true o_customer_type: type: integer description: '' example: 16 nullable: true o_is_cod: type: boolean description: 'optional Whether the order is COD (Cash on Delivery).' example: true nullable: true o_shipping_price: type: number description: 'optional Customer shipping cost.' example: 12.5 nullable: true o_shipping_cost: type: number description: 'optional Delivery cost for company.' example: 9.8 nullable: true o_sum: type: string description: 'Эти поля рассчитываются — запрещаем передавать.' example: null nullable: false o_cod_sum: type: string description: '' example: null nullable: false o_company_id: type: integer description: 'optional Reference to rw_companies.' example: 44 nullable: true contact: type: object description: 'optional Recipient information.' example: [] nullable: true properties: oc_first_name: type: string description: 'optional First name.' example: John nullable: true oc_middle_name: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true oc_last_name: type: string description: 'optional Last name.' example: Doe nullable: true oc_phone: type: string description: 'optional Phone number.' example: '+15551234567' nullable: true oc_email: type: string description: 'optional Email address.' example: john@example.com nullable: true oc_country_id: type: integer description: '' example: 16 nullable: true oc_city_id: type: integer description: '' example: 16 nullable: true oc_postcode: type: string description: 'Must not be greater than 20 characters.' example: ngzmiyvdljnikhwa nullable: true oc_coord_latitude: type: number description: '' example: 4326.41688 nullable: true oc_coord_longitude: type: number description: '' example: 4326.41688 nullable: true oc_full_address: type: string description: 'optional Full address.' example: '"221B Baker Street, London"' nullable: true ds: type: object description: 'optional Delivery information.' example: [] nullable: true properties: ods_ds_id: type: integer description: 'optional Delivery service ID.' example: 12 nullable: true ods_status: type: integer description: 'optional Delivery status code.' example: 1 nullable: true ods_track_number: type: string description: 'optional Tracking number.' example: '"TRACK-12345"' nullable: true ods_ds_pp_id: type: string description: 'Must not be greater than 100 characters.' example: g nullable: true ods_source_id: type: integer description: '' example: 16 nullable: true required: - o_type_id - o_shop_id - o_wh_id '/api/orders/{id}': get: summary: 'Get a single order by ID' operationId: getASingleOrderByID description: "Returns detailed information about a specific order including offers,\ncontact, and delivery info." parameters: [] responses: 200: description: Found content: application/json: schema: type: object example: o_id: 105 o_ext_id: ORD-105 o_type_id: 2 o_shop_id: 1 o_wh_id: 2 contact: oc_first_name: John oc_phone: '+15551234567' ds: ods_track_number: TRACK-12345 offers: - oo_offer_id: 77 oo_qty: 2 oo_price: 39.95 properties: o_id: type: integer example: 105 o_ext_id: type: string example: ORD-105 o_type_id: type: integer example: 2 o_shop_id: type: integer example: 1 o_wh_id: type: integer example: 2 contact: type: object properties: oc_first_name: type: string example: John oc_phone: type: string example: '+15551234567' ds: type: object properties: ods_track_number: type: string example: TRACK-12345 offers: type: array example: - oo_offer_id: 77 oo_qty: 2 oo_price: 39.95 items: type: object properties: oo_offer_id: type: integer example: 77 oo_qty: type: integer example: 2 oo_price: type: number example: 39.95 404: description: '' content: application/json: schema: type: object example: message: 'Order not found' properties: message: type: string example: 'Order not found' tags: - Orders put: summary: 'Update an existing order' operationId: updateAnExistingOrder description: "Updates editable fields of an existing order.\nAutomatically recalculates totals and prevents modification of computed fields." parameters: [] responses: 200: description: Updated content: application/json: schema: type: object example: o_id: 105 o_status_id: 15 o_date_send: '2025-11-15' properties: o_id: type: integer example: 105 o_status_id: type: integer example: 15 o_date_send: type: string example: '2025-11-15' 404: description: '' content: application/json: schema: type: object example: message: 'Order not found' properties: message: type: string example: 'Order not found' tags: - Orders requestBody: required: false content: application/json: schema: type: object properties: o_sum: type: string description: '' example: null nullable: false o_cod_sum: type: string description: '' example: null nullable: false o_status_id: type: integer description: 'optional New order status.' example: 15 nullable: false o_date_send: type: date description: 'optional Updated shipping date.' example: '2025-11-15' nullable: false o_shipping_price: type: number description: 'optional Updated customer shipping price.' example: 15.5 nullable: false delete: summary: 'Delete an order' operationId: deleteAnOrder description: "Deletes a specific order and all associated offers.\nOnly accessible to authorized users within their domain." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: Deleted properties: message: type: string example: Deleted 404: description: '' content: application/json: schema: type: object example: message: 'Order not found' properties: message: type: string example: 'Order not found' tags: - Orders parameters: - in: path name: id description: 'The order ID.' example: 105 required: true schema: type: integer '/api/orders/{id}/offers': post: summary: 'Add offers (products) to an order' operationId: addOffersproductsToAnOrder description: "Adds one or more product offers to an existing order.\nThe offer list must include product IDs and quantities." parameters: [] responses: 201: description: Added content: application/json: schema: type: object example: offers: - oo_offer_id: 77 oo_qty: 2 oo_price: 39.95 properties: offers: type: array example: - oo_offer_id: 77 oo_qty: 2 oo_price: 39.95 items: type: object properties: oo_offer_id: type: integer example: 77 oo_qty: type: integer example: 2 oo_price: type: number example: 39.95 tags: - Orders requestBody: required: true content: application/json: schema: type: object properties: offers: type: array description: 'List of offers to add.' example: - architecto items: type: string required: - offers parameters: - in: path name: id description: 'The order ID.' example: 105 required: true schema: type: integer '/api/orders/{order_id}/offers/{offer_id}': put: summary: 'Update an offer within an order' operationId: updateAnOfferWithinAnOrder description: 'Updates quantity or price for a specific offer line.' parameters: [] responses: 200: description: Updated content: application/json: schema: type: object example: message: 'Offer updated' properties: message: type: string example: 'Offer updated' tags: - Orders requestBody: required: false content: application/json: schema: type: object properties: oo_qty: type: number description: 'optional Updated quantity.' example: 5.0 nullable: false oo_oc_price: type: number description: '' example: 4326.41688 nullable: false oo_price: type: number description: 'optional Updated unit price.' example: 45.0 nullable: false oo_expiration_date: type: string description: 'Must be a valid date.' example: '2025-11-12T11:51:33' nullable: true oo_batch: type: string description: 'Must not be greater than 15 characters.' example: i nullable: true oo_operation_user_id: type: integer description: '' example: 16 nullable: true delete: summary: 'Delete an offer from an order' operationId: deleteAnOfferFromAnOrder description: 'Removes a specific offer from an existing order.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: 'Offer removed from order' properties: message: type: string example: 'Offer removed from order' 404: description: '' content: application/json: schema: type: object example: error: 'Offer not found in this order' properties: error: type: string example: 'Offer not found in this order' tags: - Orders parameters: - in: path name: order_id description: 'The order ID.' example: 105 required: true schema: type: integer - in: path name: offer_id description: 'Offer ID inside this order.' example: 44 required: true schema: type: integer /api/products: get: summary: 'Get product list' operationId: getProductList description: "Returns a paginated list of products (offers) with filtering and sorting.\nNon-admin access is restricted to the caller's domain and (unless the user has the `warehouse_manager` role)\nto shops associated with the caller or their parent account (see \"Domain & Access Scoping\" above).\n\n#### Pagination\n- `per_page` is capped at **250** to protect performance.\n\n#### Sorting\n- Use `sort_by` with any sortable column (e.g. `of_name`, `of_price`, `of_status`).\n- Use `sort_dir` as `asc` or `desc`. Defaults to `asc`." parameters: - in: query name: page description: 'The page number for pagination.' example: 1 required: false schema: type: integer description: 'The page number for pagination.' example: 1 nullable: false - in: query name: per_page description: 'Number of items per page (max 250).' example: 50 required: false schema: type: integer description: 'Number of items per page (max 250).' example: 50 nullable: false - in: query name: of_id description: 'Filter by internal numeric ID.' example: 1024 required: false schema: type: integer description: 'Filter by internal numeric ID.' example: 1024 nullable: false - in: query name: of_ext_id description: 'Filter by external ID (third-party mapping). Max 25 chars.' example: EXT-ACME-777 required: false schema: type: string description: 'Filter by external ID (third-party mapping). Max 25 chars.' example: EXT-ACME-777 nullable: false - in: query name: of_sku description: 'Filter by SKU (exact match). Max 25 chars.' example: SKU-001 required: false schema: type: string description: 'Filter by SKU (exact match). Max 25 chars.' example: SKU-001 nullable: false - in: query name: of_name description: 'Filter by product name (substring, case-insensitive). Max 150 chars.' example: shirt required: false schema: type: string description: 'Filter by product name (substring, case-insensitive). Max 150 chars.' example: shirt nullable: false - in: query name: of_status description: 'Filter by status (1 = active, 0 = inactive).' example: 1 required: false schema: type: integer description: 'Filter by status (1 = active, 0 = inactive).' example: 1 nullable: false - in: query name: sort_by description: 'Field to sort by. Common values: `of_name`, `of_price`, `of_status`, `created_at`.' example: of_price required: false schema: type: string description: 'Field to sort by. Common values: `of_name`, `of_price`, `of_status`, `created_at`.' example: of_price nullable: false - in: query name: sort_dir description: 'Sort direction. Allowed: `asc`, `desc`. Defaults to `asc`.' example: desc required: false schema: type: string description: 'Sort direction. Allowed: `asc`, `desc`. Defaults to `asc`.' example: desc nullable: false responses: 200: description: 'Success (paginated list)' content: text/plain: schema: type: string example: "{\n \"data\": [\n {\n \"of_id\": 10, // Example product ID\n \"of_ext_id\": \"EXT-123\",\n \"of_name\": \"Cotton T-shirt\",\n \"of_sku\": \"SKU-001\",\n \"of_article\": \"ART-444\",\n \"of_price\": 19.99,\n \"of_status\": 1,\n \"of_shop_id\": 15,\n \"of_domain_id\": 3,\n \"of_dimensions\": { \"x\": 20, \"y\": 10, \"z\": 5 },\n \"of_weight\": 350,\n \"of_image\": \"https://cdn.example.com/images/sku001.jpg\",\n \"of_datamatrix\": 1234567890123,\n \"of_comment\": \"New seasonal item\",\n \"rests\":\n {\n \"10\": 120 // when 10 - ID warehouse, 120 - quantity in warehouse\n },\n \"created_at\": \"2025-01-10T09:30:00Z\",\n \"updated_at\": \"2025-02-01T16:20:00Z\"\n }\n ],\n \"links\": {\n \"first\": \"https://api.example.com/api/offers?page=1\",\n \"last\": \"https://api.example.com/api/offers?page=3\",\n \"prev\": null,\n \"next\": \"https://api.example.com/api/offers?page=2\"\n },\n \"meta\": {\n \"current_page\": 1,\n \"from\": 1,\n \"last_page\": 3,\n \"path\": \"https://api.example.com/api/offers\",\n \"per_page\": 50,\n \"to\": 50,\n \"total\": 123\n }\n}" tags: - Products post: summary: 'Create products (bulk)' operationId: createProductsbulk description: "Creates multiple products in a single request.\n\n#### Access rules\n- Admins can create products in any shop within any domain.\n- `warehouse_manager` can create products **only** in shops within their `domain_id`.\n- Regular users can create products only in shops where `sh_user_id` ∈ { current_user.id, current_user.parent_id } and within the same domain.\n\n#### Notes\n- Server will set `of_domain_id` automatically to the caller's domain (for non-admins).\n- Server will set `of_status` to **1** (active) for newly created offers unless overridden by privileged logic.\n- Omit computed fields (e.g., `rests`)." parameters: [] responses: 201: description: 'Created (array of created products as resources)' content: application/json: schema: type: array items: type: object properties: of_id: type: integer example: 1 of_ext_id: type: string example: EXT-123 of_name: type: string example: 'Cotton T-shirt' of_sku: type: string example: SKU-001 of_article: type: string example: ART-444 of_price: type: number example: 19.99 of_status: type: integer example: 1 of_shop_id: type: integer example: 15 of_domain_id: type: integer example: 3 of_dimensions: type: object properties: x: type: integer example: 20 'y': type: integer example: 10 z: type: integer example: 5 of_weight: type: integer example: 350 of_image: type: string example: 'https://cdn.example.com/images/sku001.jpg' of_datamatrix: type: integer example: 1234567890123 of_comment: type: string example: 'New seasonal item' rests: type: object properties: total: type: integer example: 0 by_places: type: object properties: { } created_at: type: string example: '2025-02-12T10:00:00Z' updated_at: type: string example: '2025-02-12T10:00:00Z' example: - of_id: 1 of_ext_id: EXT-123 of_name: 'Cotton T-shirt' of_sku: SKU-001 of_article: ART-444 of_price: 19.99 of_status: 1 of_shop_id: 15 of_domain_id: 3 of_dimensions: x: 20 'y': 10 z: 5 of_weight: 350 of_image: 'https://cdn.example.com/images/sku001.jpg' of_datamatrix: 1234567890123 of_comment: 'New seasonal item' rests: total: 0 by_places: { } created_at: '2025-02-12T10:00:00Z' updated_at: '2025-02-12T10:00:00Z' 401: description: 'Insufficient rights' content: application/json: schema: type: object example: status: error message: 'You do not have sufficient rights to create a product in shop ID 15' properties: status: type: string example: error message: type: string example: 'You do not have sufficient rights to create a product in shop ID 15' 422: description: 'Validation error example' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: 0.of_shop_id: - 'The of_shop_id field is required.' 0.of_name: - 'The of_name field is required.' 0.of_name.1: - 'The of_name may not be greater than 150 characters.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: 0.of_shop_id: type: array example: - 'The of_shop_id field is required.' items: type: string 0.of_name: type: array example: - 'The of_name field is required.' items: type: string 0.of_name.1: type: array example: - 'The of_name may not be greater than 150 characters.' items: type: string tags: - Products requestBody: required: true content: application/json: schema: type: object properties: products: type: array description: 'Array of product objects to create.' example: - architecto items: type: string required: - products '/api/products/{id}': get: summary: 'Get a single product' operationId: getASingleProduct description: 'Returns a detailed product (offer) by its ID, scoped to the caller’s domain and shop access (see class-level notes).' parameters: [] responses: 200: description: Found content: application/json: schema: type: object example: of_id: 10 of_ext_id: EXT-123 of_name: 'Cotton T-shirt' of_sku: SKU-001 of_article: ART-444 of_price: 19.99 of_status: 1 of_shop_id: 15 of_domain_id: 3 of_dimensions: x: 20 'y': 10 z: 5 of_weight: 350 of_image: 'https://cdn.example.com/images/sku001.jpg' of_datamatrix: 1234567890123 of_comment: Seasonal rests: total: 120 by_places: A-01-01: 50 A-01-02: 70 created_at: '2025-01-10T09:30:00Z' updated_at: '2025-02-01T16:20:00Z' properties: of_id: type: integer example: 10 of_ext_id: type: string example: EXT-123 of_name: type: string example: 'Cotton T-shirt' of_sku: type: string example: SKU-001 of_article: type: string example: ART-444 of_price: type: number example: 19.99 of_status: type: integer example: 1 of_shop_id: type: integer example: 15 of_domain_id: type: integer example: 3 of_dimensions: type: object properties: x: type: integer example: 20 'y': type: integer example: 10 z: type: integer example: 5 of_weight: type: integer example: 350 of_image: type: string example: 'https://cdn.example.com/images/sku001.jpg' of_datamatrix: type: integer example: 1234567890123 of_comment: type: string example: Seasonal rests: type: object properties: total: type: integer example: 120 by_places: type: object properties: A-01-01: type: integer example: 50 A-01-02: type: integer example: 70 created_at: type: string example: '2025-01-10T09:30:00Z' updated_at: type: string example: '2025-02-01T16:20:00Z' 404: description: 'Not found or out of scope' content: application/json: schema: type: object example: message: 'Not found' properties: message: type: string example: 'Not found' tags: - Products put: summary: 'Update a product' operationId: updateAProduct description: "Partially updates product fields (PATCH semantics; all provided fields will be updated).\nThe record must belong to the caller’s domain; otherwise, `404 Not found` is returned." parameters: [] responses: 200: description: Updated content: application/json: schema: type: object example: of_id: 10 of_name: 'Updated T-shirt' of_price: 24.5 of_status: 1 updated_at: '2025-02-12T12:00:00Z' properties: of_id: type: integer example: 10 of_name: type: string example: 'Updated T-shirt' of_price: type: number example: 24.5 of_status: type: integer example: 1 updated_at: type: string example: '2025-02-12T12:00:00Z' 404: description: "Not found in caller's domain" content: application/json: schema: type: object example: message: 'Not found' properties: message: type: string example: 'Not found' 422: description: 'Validation error (example)' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: of_name: - 'The of_name may not be greater than 150 characters.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: of_name: type: array example: - 'The of_name may not be greater than 150 characters.' items: type: string tags: - Products requestBody: required: false content: application/json: schema: type: object properties: of_name: type: string description: 'Product name. Max: 150 chars.' example: '"Updated T-shirt"' nullable: false of_article: type: string description: 'Internal article number. Max: 25 chars.' example: '"ART-444"' nullable: false of_sku: type: string description: 'SKU. Max: 25 chars.' example: '"SKU-001"' nullable: false of_price: type: number description: 'Unit price.' example: 24.5 nullable: false of_estimated_price: type: number description: 'Estimated price for analytics.' example: 22.0 nullable: false of_img: type: string description: 'Image URL or path. Max: 255 chars.' example: '"https://cdn.example.com/images/sku001-v2.jpg"' nullable: false of_dimension_x: type: number description: 'Length in centimeters.' example: 21.0 nullable: false of_dimension_y: type: number description: 'Width in centimeters.' example: 11.0 nullable: false of_dimension_z: type: number description: 'Height in centimeters.' example: 6.0 nullable: false of_weight: type: integer description: 'Weight in grams.' example: 360 nullable: false of_datamatrix: type: integer description: 'DataMatrix code.' example: 1234567890123 nullable: false of_comment: type: string description: 'Comment. Max: 255 chars.' example: '"Price adjusted"' nullable: false of_status: type: integer description: 'Product status. Allowed: 0 (inactive), 1 (active).' example: 1 nullable: false delete: summary: 'Delete a product' operationId: deleteAProduct description: "Deletes a product by ID. To prevent accidental cross-shop deletions, the controller also checks that the product’s\n`of_shop_id` matches the caller’s `shop_id` (in addition to domain scoping). If the record is outside of the caller’s scope,\na `404 Not found` is returned." parameters: [] responses: 200: description: Deleted content: application/json: schema: type: object example: message: Deleted properties: message: type: string example: Deleted 404: description: 'Not found or out of scope' content: application/json: schema: type: object example: message: 'Not found' properties: message: type: string example: 'Not found' tags: - Products parameters: - in: path name: id description: 'Product ID.' example: 10 required: true schema: type: integer /api/user: get: summary: '' operationId: getApiUser description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Users security: [] /api/login: post: summary: '' operationId: postApiLogin description: '' parameters: [] responses: { } tags: - Users security: [] /api/logout: post: summary: '' operationId: postApiLogout description: '' parameters: [] responses: { } tags: - Users security: [] /api/accounts/create: post: summary: '' operationId: postApiAccountsCreate description: '' parameters: [] responses: { } tags: - Users requestBody: required: true content: application/json: schema: type: object properties: language: type: string description: 'Must not be greater than 6 characters.' example: bngz nullable: false company: type: string description: 'Must not be greater than 100 characters.' example: m nullable: false FName: type: string description: 'Must not be greater than 255 characters.' example: i nullable: false LName: type: string description: 'Must not be greater than 255 characters.' example: 'y' nullable: false email: type: string description: 'Must be a valid email address.' example: justina.gaylord@example.org nullable: false password: type: string description: 'Must be at least 6 characters.' example: 'gxwmi/#iw/' nullable: false massage: type: string description: '' example: architecto nullable: true fst: type: number description: '' example: 4326.41688 nullable: false mName: type: string description: '' example: architecto nullable: true required: - language - company - FName - LName - email - password - fst security: []