@restforgejs/platform 4.2.8 → 4.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SECURITY.md +83 -4
- package/bin/sdf-tools.exe +0 -0
- package/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/dashboard/create.js +4 -1
- package/generators/cli/endpoint/create.js +1 -1
- package/generators/cli/key/generate.js +2 -1
- package/generators/cli/key/revoke.js +2 -1
- package/generators/cli/payload/diff.js +3 -2
- package/generators/cli/payload/generate.js +3 -2
- package/generators/cli/payload/sync.js +3 -2
- package/generators/cli/payload/validate.js +3 -2
- package/generators/cli/processor/create.js +14 -3
- package/generators/cli/project/delete.js +2 -1
- package/generators/cli/query/validate.js +3 -2
- package/generators/cli/schema/apply.js +3 -2
- package/generators/cli/schema/describe.js +3 -2
- package/generators/cli/schema/diff.js +3 -2
- package/generators/cli/schema/introspect.js +3 -2
- package/generators/cli/schema/list.js +3 -2
- package/generators/cli/schema/migrate.js +3 -2
- package/generators/lib/migration/audit-table-runner.js +213 -215
- package/generators/lib/payload/payload-runner.js +1 -1
- package/generators/lib/templates/dashboard-catalog.js +1 -437
- package/generators/lib/templates/db-connection-env.js +1 -212
- package/generators/lib/templates/dbschema-catalog.js +1 -489
- package/generators/lib/templates/field-validation-catalog.js +1 -531
- package/generators/lib/templates/mysql-template.js +1 -3863
- package/generators/lib/templates/oracle-template.js +1 -3915
- package/generators/lib/templates/postgres-template.js +1 -5838
- package/generators/lib/templates/query-declarative-catalog.js +1 -199
- package/generators/lib/templates/sqlite-template.js +1 -3440
- package/generators/lib/utils/env-manager.js +6 -0
- package/generators/lib/utils/path-validator.js +71 -0
- package/generators/lib/validators/payload-validator.js +1 -2
- package/integrity-manifest.json +28 -10
- package/package.json +11 -3
- package/scripts/verify-integrity.js +1 -1
- package/server.js +1 -1
- package/src/components/handlers/adjust_handler.js +1 -1
- package/src/components/handlers/audit_handler.js +1 -1
- package/src/components/handlers/delete_handler.js +1 -1
- package/src/components/handlers/export_handler.js +1 -1
- package/src/components/handlers/import_handler.js +1 -1
- package/src/components/handlers/insert_handler.js +1 -1
- package/src/components/handlers/update_handler.js +1 -1
- package/src/components/handlers/upload_handler.js +1 -1
- package/src/components/handlers/workflow_handler.js +1 -1
- package/src/components/integrations/webhook.js +1 -1
- package/src/consumers/baseConsumer.js +1 -1
- package/src/consumers/declarativeMapper.js +1 -1
- package/src/consumers/handlers/apiHandler.js +1 -1
- package/src/consumers/handlers/consoleHandler.js +1 -1
- package/src/consumers/handlers/databaseHandler.js +1 -1
- package/src/consumers/handlers/index.js +1 -1
- package/src/consumers/handlers/kafkaHandler.js +1 -1
- package/src/consumers/index.js +1 -1
- package/src/consumers/messageTransformer.js +1 -1
- package/src/consumers/validator.js +1 -1
- package/src/core/db/dialect/base-dialect.js +1 -1
- package/src/core/db/dialect/index.js +1 -1
- package/src/core/db/dialect/mysql-dialect.js +1 -1
- package/src/core/db/dialect/oracle-dialect.js +1 -1
- package/src/core/db/dialect/postgres-dialect.js +1 -1
- package/src/core/db/dialect/sqlite-dialect.js +1 -1
- package/src/core/db/flatten-helper.js +1 -1
- package/src/core/db/query-builder-error.js +1 -1
- package/src/core/db/query-builder.js +1 -1
- package/src/core/db/relation-helper.js +1 -1
- package/src/core/handlers/delete_handler.js +1 -1
- package/src/core/handlers/insert_handler.js +1 -1
- package/src/core/handlers/update_handler.js +1 -1
- package/src/core/models/base-model.js +1 -1
- package/src/core/utils/cache-manager.js +1 -1
- package/src/core/utils/component-engine.js +1 -1
- package/src/core/utils/context-builder.js +1 -1
- package/src/core/utils/datetime-formatter.js +1 -1
- package/src/core/utils/datetime-parser.js +1 -1
- package/src/core/utils/db.js +1 -1
- package/src/core/utils/logger.js +1 -1
- package/src/core/utils/payload-loader.js +1 -1
- package/src/core/utils/security-checks.js +1 -1
- package/src/middleware/body-options.js +1 -1
- package/src/middleware/cors.js +1 -1
- package/src/middleware/idempotency.js +1 -1
- package/src/middleware/rate-limiter.js +1 -1
- package/src/middleware/request-logger.js +1 -1
- package/src/middleware/security-headers.js +1 -1
- package/src/models/base-model-mysql.js +1 -1
- package/src/models/base-model-oracle.js +1 -1
- package/src/models/base-model-sqlite.js +1 -1
- package/src/models/base-model.js +1 -1
- package/src/pro/caching/redis-client.js +1 -1
- package/src/pro/caching/redis-helper.js +1 -1
- package/src/pro/consumers/baseConsumer.js +1 -1
- package/src/pro/consumers/declarativeMapper.js +1 -1
- package/src/pro/consumers/handlers/apiHandler.js +1 -1
- package/src/pro/consumers/handlers/consoleHandler.js +1 -1
- package/src/pro/consumers/handlers/databaseHandler.js +1 -1
- package/src/pro/consumers/handlers/index.js +1 -1
- package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
- package/src/pro/consumers/index.js +1 -1
- package/src/pro/consumers/messageTransformer.js +1 -1
- package/src/pro/consumers/validator.js +1 -1
- package/src/pro/database/base-model-mysql.js +1 -1
- package/src/pro/database/base-model-oracle.js +1 -1
- package/src/pro/database/base-model-sqlite.js +1 -1
- package/src/pro/database/db-mysql.js +1 -1
- package/src/pro/database/db-oracle.js +1 -1
- package/src/pro/database/db-sqlite.js +1 -1
- package/src/pro/excel/excel-generator.js +1 -1
- package/src/pro/excel/excel-parser.js +1 -1
- package/src/pro/excel/export-service.js +1 -1
- package/src/pro/excel/export_handler.js +1 -1
- package/src/pro/excel/import-service.js +1 -1
- package/src/pro/excel/import-validator.js +1 -1
- package/src/pro/excel/import_handler.js +1 -1
- package/src/pro/excel/upsert-builder.js +1 -1
- package/src/pro/idgen/idgen-routes.js +1 -1
- package/src/pro/integrations/lookup-resolver.js +1 -1
- package/src/pro/integrations/upload-handler-v2.js +1 -1
- package/src/pro/integrations/upload-handler.js +1 -1
- package/src/pro/integrations/webhook.js +1 -1
- package/src/pro/locking/lock-routes.js +1 -1
- package/src/pro/locking/resource-lock-manager.js +1 -1
- package/src/pro/messaging/kafkaConsumerService.js +1 -1
- package/src/pro/messaging/kafkaService.js +1 -1
- package/src/pro/messaging/messagehubService.js +1 -1
- package/src/pro/messaging/rabbitmqService.js +1 -1
- package/src/pro/scheduler/job-manager.js +1 -1
- package/src/pro/scheduler/job-routes.js +1 -1
- package/src/pro/scheduler/job-validator.js +1 -1
- package/src/pro/storage/base-storage-provider.js +1 -1
- package/src/pro/storage/file-metadata-helper.js +1 -1
- package/src/pro/storage/index.js +1 -1
- package/src/pro/storage/local-storage-provider.js +1 -1
- package/src/pro/storage/s3-storage-provider.js +1 -1
- package/src/pro/storage/upload-cleanup-job.js +1 -1
- package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
- package/src/pro/storage/upload-pending-tracker.js +1 -1
- package/src/pro/websocket/broadcast-helper.js +1 -1
- package/src/pro/websocket/index.js +1 -1
- package/src/pro/websocket/livesync-server.js +1 -1
- package/src/pro/websocket/ws-broadcaster.js +1 -1
- package/src/services/export-service.js +1 -1
- package/src/services/import-service.js +1 -1
- package/src/services/kafkaConsumerService.js +1 -1
- package/src/services/kafkaService.js +1 -1
- package/src/services/messagehubService.js +1 -1
- package/src/services/rabbitmqService.js +1 -1
- package/src/utils/cache-invalidation-registry.js +1 -1
- package/src/utils/cache-manager.js +1 -1
- package/src/utils/component-engine.js +1 -1
- package/src/utils/config-extractor.js +1 -1
- package/src/utils/consumerLogger.js +1 -1
- package/src/utils/context-builder.js +1 -1
- package/src/utils/dashboard-helpers.js +1 -1
- package/src/utils/dateHelper.js +1 -1
- package/src/utils/datetime-formatter.js +1 -1
- package/src/utils/datetime-parser.js +1 -1
- package/src/utils/db-bootstrap.js +1 -1
- package/src/utils/db-mysql.js +1 -1
- package/src/utils/db-oracle.js +1 -1
- package/src/utils/db-sqlite.js +1 -1
- package/src/utils/db.js +1 -1
- package/src/utils/demo-generator.js +1 -1
- package/src/utils/excel-generator.js +1 -1
- package/src/utils/excel-parser.js +1 -1
- package/src/utils/file-watcher.js +1 -1
- package/src/utils/id-generator.js +1 -1
- package/src/utils/idempotency-manager.js +1 -1
- package/src/utils/import-validator.js +1 -1
- package/src/utils/license-client.js +1 -1
- package/src/utils/lock-manager.js +1 -1
- package/src/utils/logger.js +1 -1
- package/src/utils/lookup-resolver.js +1 -1
- package/src/utils/payload-loader.js +1 -1
- package/src/utils/processor-response.js +1 -1
- package/src/utils/rabbitmq.js +1 -1
- package/src/utils/redis-client.js +1 -1
- package/src/utils/redis-helper.js +1 -1
- package/src/utils/request-scope.js +1 -1
- package/src/utils/security-checks.js +1 -1
- package/src/utils/service-resolver.js +1 -1
- package/src/utils/shutdown-coordinator.js +1 -1
- package/src/utils/trusted-keys.js +1 -1
- package/src/utils/upload-handler.js +1 -1
- package/src/utils/upsert-builder.js +1 -1
- package/src/utils/workflow-hook-executor.js +1 -1
- package/generators/metadata/global.json +0 -58
- package/generators/metadata/test-mysql-workbench.json +0 -118
- package/generators/metadata/test-mysql.json +0 -56
- package/generators/metadata/test-oracle-workbench.json +0 -118
- package/generators/metadata/test-oracle.json +0 -56
- package/generators/metadata/test-pg-workbench.json +0 -118
- package/generators/metadata/test-pg.json +0 -56
- package/generators/scripts/obfuscate-source.js +0 -356
- package/generators/scripts/validate-catalog.js +0 -430
- package/generators/scripts/validate-dbschema-catalog.js +0 -708
- package/generators/tests/baseline/mysql/mini_inventory_item/src/models/mini-inventory/item.js +0 -944
- package/generators/tests/baseline/mysql/mini_inventory_item/src/modules/mini-inventory/item.js +0 -740
- package/generators/tests/baseline/mysql/mini_inventory_item/src/modules/mini-inventory.js +0 -336
- package/generators/tests/baseline/oracle/mini_inventory_item/src/models/mini-inventory/item.js +0 -1002
- package/generators/tests/baseline/oracle/mini_inventory_item/src/modules/mini-inventory/item.js +0 -740
- package/generators/tests/baseline/oracle/mini_inventory_item/src/modules/mini-inventory.js +0 -336
- package/generators/tests/baseline/postgres/mini_inventory_item/src/models/mini-inventory/item.js +0 -1333
- package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory/item.js +0 -1173
- package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory.js +0 -496
- package/generators/tests/fixtures/payloads/custom-sensitive.json +0 -27
- package/generators/tests/fixtures/payloads/dynamic-search-optout.json +0 -23
- package/generators/tests/fixtures/payloads/login-with-password.json +0 -22
- package/generators/tests/fixtures/payloads/order-process.json +0 -52
- package/generators/tests/fixtures/payloads/with-inline-sql.json +0 -26
- package/generators/tests/integration-tahap4b/README.md +0 -145
- package/generators/tests/integration-tahap4b/run-concurrent.js +0 -77
- package/generators/tests/integration-tahap4b/seed.sql +0 -53
- package/generators/tests/integration-tahap4b/verify.sql +0 -110
- package/generators/tests/unit/cli/create-dashboard.test.js +0 -505
- package/generators/tests/unit/cli/create-processor.test.js +0 -319
- package/generators/tests/unit/cli/dispatch-dashboard.test.js +0 -149
- package/generators/tests/unit/lib/dashboard-generator.test.js +0 -895
- package/generators/tests/unit/lib/dashboard-validator.test.js +0 -354
- package/generators/tests/unit/lib/dbschema-kit/apply-executor.test.js +0 -437
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-introspect.test.js +0 -393
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-generate-ddl.test.js +0 -104
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-init.test.js +0 -119
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-list.test.js +0 -48
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-migrate.test.js +0 -175
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-validate.test.js +0 -102
- package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-models.test.js +0 -43
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/all-schemas-listing.js +0 -84
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/connection-error.js +0 -13
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/empty.js +0 -12
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/multi-schema.js +0 -124
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/single-schema-inventory.js +0 -64
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/two-tables.js +0 -66
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/connection-error.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/partial.js +0 -29
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/rollback.js +0 -26
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/success.js +0 -43
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/audit/events.js +0 -18
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/inventory/products.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/users.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/connection.test.js +0 -112
- package/generators/tests/unit/lib/dbschema-kit/ddl-generator.test.js +0 -205
- package/generators/tests/unit/lib/dbschema-kit/define-model.test.js +0 -56
- package/generators/tests/unit/lib/dbschema-kit/dialect/index.test.js +0 -46
- package/generators/tests/unit/lib/dbschema-kit/dialect/mysql.test.js +0 -126
- package/generators/tests/unit/lib/dbschema-kit/dialect/oracle.test.js +0 -126
- package/generators/tests/unit/lib/dbschema-kit/dialect/postgres.test.js +0 -131
- package/generators/tests/unit/lib/dbschema-kit/dialect/sqlite.test.js +0 -126
- package/generators/tests/unit/lib/dbschema-kit/driver-loader.test.js +0 -93
- package/generators/tests/unit/lib/dbschema-kit/emitters/create-index.test.js +0 -173
- package/generators/tests/unit/lib/dbschema-kit/emitters/create-table.test.js +0 -376
- package/generators/tests/unit/lib/dbschema-kit/emitters/drop-table.test.js +0 -78
- package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/invalid-dialect.env +0 -6
- package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/missing-dialect.env +0 -5
- package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/missing-host.env +0 -5
- package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/oracle-valid.env +0 -6
- package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/postgres-valid.env +0 -7
- package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/sqlite-valid.env +0 -2
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/category.js +0 -11
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/item_product.js +0 -11
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/stock_inbound.js +0 -24
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/stock_inbound_item.js +0 -28
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/supplier.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/warehouse.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-invalid/orphan.js +0 -17
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/category.js +0 -11
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/item_product.js +0 -11
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/supplier.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/warehouse.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/transactions/stock_inbound.js +0 -24
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/transactions/stock_inbound_item.js +0 -28
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/audit/events.js +0 -18
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/inventory/products.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/public/users.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-subfolder/extra/category.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-subfolder/master/category.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-tablename/bar.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-tablename/foo.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/empty-folder/README.md +0 -1
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/invalid-export/plain.js +0 -3
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/invalid-schema/bad.js +0 -6
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/legacy-pattern/legacy.js +0 -12
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-distinct/audit/products.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-distinct/inventory/products.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-duplicate/a/products.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-duplicate/b/products.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/nested-deep/a/b/c/deep_table.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/.hidden/ignored.js +0 -7
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/master/category.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/master/supplier.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/transactions/stock_inbound.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/transactions/stock_inbound_item.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-multiple/category.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-multiple/item_product.js +0 -9
- package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-single/category.js +0 -8
- package/generators/tests/unit/lib/dbschema-kit/integration.test.js +0 -217
- package/generators/tests/unit/lib/dbschema-kit/introspect-mapper.test.js +0 -403
- package/generators/tests/unit/lib/dbschema-kit/ir-builder.test.js +0 -390
- package/generators/tests/unit/lib/dbschema-kit/loader.test.js +0 -128
- package/generators/tests/unit/lib/dbschema-kit/naming.test.js +0 -170
- package/generators/tests/unit/lib/dbschema-kit/parser/shorthand-parser.test.js +0 -237
- package/generators/tests/unit/lib/dbschema-kit/schema-printer.test.js +0 -251
- package/generators/tests/unit/lib/dbschema-kit/statement-modifier.test.js +0 -105
- package/generators/tests/unit/lib/dbschema-kit/statement-splitter.test.js +0 -165
- package/generators/tests/unit/lib/dbschema-kit/topological-sort.test.js +0 -135
- package/generators/tests/unit/lib/dbschema-kit/validator/check-compatibility-validator.test.js +0 -373
- package/generators/tests/unit/lib/dbschema-kit/validator/circular-relation-validator.test.js +0 -454
- package/generators/tests/unit/lib/dbschema-kit/validator/cross-model-validator.test.js +0 -512
- package/generators/tests/unit/lib/dbschema-kit/validator/enhanced-validate-integration.test.js +0 -390
- package/generators/tests/unit/lib/dbschema-kit/validator/naming-convention-validator.test.js +0 -306
- package/generators/tests/unit/lib/dbschema-kit/validator/schema-validator.test.js +0 -443
- package/generators/tests/unit/lib/dbschema-kit/validator/type-compatibility-validator.test.js +0 -440
- package/generators/tests/unit/lib/dbschema-kit/validator/validator-reporter.test.js +0 -172
- package/generators/tests/unit/lib/metadata-manager-dashboard.test.js +0 -256
- package/generators/tests/unit/lib/payload-validator-fieldpolicy.test.js +0 -240
- package/generators/tests/unit/lib/processor-validation-generator.test.js +0 -300
- package/generators/tests/unit/lib/sensitive-field-masker.test.js +0 -170
- package/generators/tests/unit/lib/sql-table-extractor.test.js +0 -119
- package/scripts/generate-integrity-manifest.js +0 -124
- package/scripts/snapshot-cli-contracts.js +0 -194
- package/scripts/verify-publish.js +0 -56
package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory/item.js
DELETED
|
@@ -1,1173 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const router = express.Router();
|
|
3
|
-
const itemModel = require('../../models/mini-inventory/item');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// Component Engine untuk event lifecycle (optional)
|
|
7
|
-
let componentEngine = null;
|
|
8
|
-
let ContextBuilder = null;
|
|
9
|
-
try {
|
|
10
|
-
// Hanya load component engine jika payload memiliki components
|
|
11
|
-
const hasComponents = undefined && Array.isArray(undefined) && undefined.length > 0;
|
|
12
|
-
|
|
13
|
-
if (hasComponents) {
|
|
14
|
-
componentEngine = require('restforgejs/src/utils/component-engine').componentEngine;
|
|
15
|
-
ContextBuilder = require('restforgejs/src/utils/context-builder');
|
|
16
|
-
|
|
17
|
-
// Load component configuration dari payload yang sedang digunakan
|
|
18
|
-
const componentConfig = {
|
|
19
|
-
tableName: 'core.item',
|
|
20
|
-
fieldName: ["item_id","item_code","item_name","description","uom","unit_price","weight","is_active","created_at","created_by","updated_at","updated_by"],
|
|
21
|
-
exportQuery: null,
|
|
22
|
-
columnFormats: null,
|
|
23
|
-
fieldLabels: null,
|
|
24
|
-
components: undefined,
|
|
25
|
-
importConfig: null,
|
|
26
|
-
adjustConfig: null,
|
|
27
|
-
workflowConfig: null,
|
|
28
|
-
uploadConfig: null,
|
|
29
|
-
requestScope: null
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
componentEngine.loadConfigurationFromObject(componentConfig).then(result => {
|
|
33
|
-
if (result.success) {
|
|
34
|
-
console.log(`Component configuration loaded for mini-inventory/item: ${result.componentsLoaded} components`);
|
|
35
|
-
}
|
|
36
|
-
}).catch(err => {
|
|
37
|
-
console.error(`Failed to load component configuration for mini-inventory/item:`, err.message);
|
|
38
|
-
});
|
|
39
|
-
} else {
|
|
40
|
-
console.log(`No components defined in payload for mini-inventory/item, running without events`);
|
|
41
|
-
}
|
|
42
|
-
} catch (e) {
|
|
43
|
-
if (hasComponents) {
|
|
44
|
-
// Components dikonfigurasi tapi engine gagal load — ini error, bukan optional
|
|
45
|
-
console.error(`CRITICAL: Component engine failed to load for mini-inventory/item but components are configured:`, e.message);
|
|
46
|
-
throw e;
|
|
47
|
-
}
|
|
48
|
-
// Jika tidak ada components, silent skip wajar
|
|
49
|
-
console.log(`No component engine required for mini-inventory/item, running without events`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Item Submodule - Auto-generated on 2026-04-25 07:34:33
|
|
54
|
-
*
|
|
55
|
-
* Endpoints untuk item dengan actions: datatables, create, update, delete, lookup, read
|
|
56
|
-
* Table: core.item
|
|
57
|
-
* Fields: 12 fields
|
|
58
|
-
* Database: PostgreSQL
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
// CORS ditangani di level app oleh cors middleware (lihat konfigurasi CORS_ENABLED dan CORS_ORIGINS di .env)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Middleware untuk validasi payload pada metode POST
|
|
66
|
-
router.use((req, res, next) => {
|
|
67
|
-
if (req.method === 'POST') {
|
|
68
|
-
try {
|
|
69
|
-
// Skip validation untuk import routes (menggunakan multipart/form-data, bukan JSON)
|
|
70
|
-
if (req.path.startsWith('/import-')) {
|
|
71
|
-
return next();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Validasi umum untuk payload
|
|
75
|
-
if (!req.body || Object.keys(req.body).length === 0) {
|
|
76
|
-
return res.status(400).json({
|
|
77
|
-
success: false,
|
|
78
|
-
error: 'Missing payload',
|
|
79
|
-
message: 'Payload cannot be empty',
|
|
80
|
-
timestamp: new Date().toISOString(),
|
|
81
|
-
endpoint: '/api/mini-inventory/item' + req.path
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Validasi spesifik untuk endpoint tertentu
|
|
86
|
-
const endpoint = req.path.substring(1); // menghapus / di awal
|
|
87
|
-
|
|
88
|
-
// Endpoint get membutuhkan where dalam format {key, value} atau [{key, value}] (1 elemen)
|
|
89
|
-
if (endpoint === 'first') {
|
|
90
|
-
// Normalize: array 1 elemen -> object
|
|
91
|
-
if (Array.isArray(req.body.where) && req.body.where.length === 1) {
|
|
92
|
-
req.body.where = req.body.where[0];
|
|
93
|
-
}
|
|
94
|
-
if (!req.body.where || typeof req.body.where !== 'object' || Array.isArray(req.body.where)) {
|
|
95
|
-
return res.status(400).json({
|
|
96
|
-
success: false,
|
|
97
|
-
error: 'Invalid payload',
|
|
98
|
-
message: 'Where must be a single condition {key, value}',
|
|
99
|
-
example: {
|
|
100
|
-
"where": { "key": "field_name", "value": "field_value" },
|
|
101
|
-
"select": ["field1", "field2"]
|
|
102
|
-
},
|
|
103
|
-
timestamp: new Date().toISOString()
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
if (req.body.where.conditions || req.body.where.logic) {
|
|
107
|
-
return res.status(400).json({
|
|
108
|
-
success: false,
|
|
109
|
-
error: 'Invalid payload',
|
|
110
|
-
message: 'Advanced where format is not supported in /first endpoint. Use /read endpoint for complex queries',
|
|
111
|
-
example: {
|
|
112
|
-
"where": { "key": "field_name", "value": "field_value" }
|
|
113
|
-
},
|
|
114
|
-
timestamp: new Date().toISOString()
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Endpoint delete membutuhkan where
|
|
120
|
-
if (endpoint === 'delete' && (!req.body.where)) {
|
|
121
|
-
return res.status(400).json({
|
|
122
|
-
success: false,
|
|
123
|
-
error: 'Invalid payload',
|
|
124
|
-
message: 'DELETE payload must include a where property',
|
|
125
|
-
example: {
|
|
126
|
-
"where": [{ "key": "id", "value": "your-id-value" }]
|
|
127
|
-
},
|
|
128
|
-
timestamp: new Date().toISOString()
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Endpoint add membutuhkan data yang valid
|
|
133
|
-
if (endpoint === 'add') {
|
|
134
|
-
// Filter field yang memiliki autoGenerate dari required check
|
|
135
|
-
const autoGenerateFields = [];
|
|
136
|
-
|
|
137
|
-
const requiredFields = ['item_id']
|
|
138
|
-
.filter(field => !autoGenerateFields.includes(field));
|
|
139
|
-
|
|
140
|
-
const missingFields = requiredFields.filter(field => !req.body[field] || req.body[field] === '');
|
|
141
|
-
|
|
142
|
-
if (missingFields.length > 0) {
|
|
143
|
-
return res.status(400).json({
|
|
144
|
-
success: false,
|
|
145
|
-
error: 'Missing required fields',
|
|
146
|
-
message: `Required field(s): ${missingFields.join(', ')}`,
|
|
147
|
-
timestamp: new Date().toISOString()
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Endpoint update membutuhkan primary key
|
|
153
|
-
if (endpoint === 'update') {
|
|
154
|
-
const primaryKey = 'id';
|
|
155
|
-
if (!req.body[primaryKey]) {
|
|
156
|
-
return res.status(400).json({
|
|
157
|
-
success: false,
|
|
158
|
-
error: 'Missing required field',
|
|
159
|
-
message: `Primary key (${primaryKey}) is required for update`,
|
|
160
|
-
timestamp: new Date().toISOString()
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
} catch (error) {
|
|
166
|
-
console.error(`Error validating payload for ${req.path}:`, error);
|
|
167
|
-
return res.status(400).json({
|
|
168
|
-
success: false,
|
|
169
|
-
error: 'Invalid payload',
|
|
170
|
-
message: 'Invalid payload',
|
|
171
|
-
details: error.message,
|
|
172
|
-
timestamp: new Date().toISOString()
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
next();
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
// POST /api/mini-inventory/item/datatables - Data untuk DataTables
|
|
181
|
-
router.post('/datatables', async (req, res) => {
|
|
182
|
-
try {
|
|
183
|
-
// req.log.debug('Request body item/datatables:', JSON.stringify(req.body, null, 2));
|
|
184
|
-
|
|
185
|
-
// Extract parameters dari request
|
|
186
|
-
const options = {
|
|
187
|
-
searchValue: req.body.search?.value || req.body.searchValue || req.body.search_value || '',
|
|
188
|
-
searchBy: req.body.searchBy || req.body.search_by || 'all',
|
|
189
|
-
perPage: parseInt(req.body.length || req.body.pagination?.perpage || 10, 10),
|
|
190
|
-
start: parseInt(req.body.start || 0, 10),
|
|
191
|
-
draw: req.body.draw || '1'
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
// Handle sort_columns
|
|
195
|
-
if (req.body.sort_columns && Array.isArray(req.body.sort_columns) && req.body.sort_columns.length > 0) {
|
|
196
|
-
options.sort_columns = req.body.sort_columns.map(item => ({
|
|
197
|
-
column: item.column,
|
|
198
|
-
direction: (item.direction || 'ASC').toUpperCase()
|
|
199
|
-
}));
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Pass format DataTables parameters jika ada (fallback)
|
|
203
|
-
if (req.body['order[0][column]'] !== undefined) {
|
|
204
|
-
options['order[0][column]'] = req.body['order[0][column]'];
|
|
205
|
-
}
|
|
206
|
-
if (req.body['order[0][dir]'] !== undefined) {
|
|
207
|
-
options['order[0][dir]'] = req.body['order[0][dir]'];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Proses filter dalam format filters object
|
|
211
|
-
if (req.body.filters && typeof req.body.filters === 'object') {
|
|
212
|
-
// Filter out values that should be ignored ("all", "-", "", null, undefined)
|
|
213
|
-
const filteredFilters = {};
|
|
214
|
-
Object.keys(req.body.filters).forEach(key => {
|
|
215
|
-
const value = req.body.filters[key];
|
|
216
|
-
// Ignore filter if value is "all", "-", empty string, null, or undefined
|
|
217
|
-
if (value !== "all" && value !== "-" && value !== "" && value !== null && value !== undefined) {
|
|
218
|
-
filteredFilters[key] = value;
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// Only set filters if there are valid filters remaining
|
|
223
|
-
if (Object.keys(filteredFilters).length > 0) {
|
|
224
|
-
options.filters = filteredFilters;
|
|
225
|
-
console.log('Applied filters (ignoring "all", "-", empty values):', JSON.stringify(filteredFilters, null, 2));
|
|
226
|
-
} else {
|
|
227
|
-
console.log('All filters ignored due to "all", "-", or empty values');
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Proses parameter where dengan format advanced conditions
|
|
232
|
-
if (req.body.where && typeof req.body.where === 'object') {
|
|
233
|
-
// Validasi format where (mendukung format array legacy dan format object baru)
|
|
234
|
-
if (Array.isArray(req.body.where) || (req.body.where.conditions && Array.isArray(req.body.where.conditions))) {
|
|
235
|
-
options.where = req.body.where;
|
|
236
|
-
console.log('Applied where conditions:', JSON.stringify(req.body.where, null, 2));
|
|
237
|
-
} else {
|
|
238
|
-
console.log('Invalid where format, ignoring where parameter');
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Validasi dan sanitasi parameters
|
|
243
|
-
if (options.perPage > 1000) {
|
|
244
|
-
options.perPage = 1000; // Limit untuk mencegah overload
|
|
245
|
-
}
|
|
246
|
-
if (options.perPage < 1) {
|
|
247
|
-
options.perPage = 10;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Gunakan model untuk mendapatkan data
|
|
251
|
-
const result = await itemModel.getDatatables(options);
|
|
252
|
-
|
|
253
|
-
// Menambahkan nomor baris untuk DataTables jika diperlukan
|
|
254
|
-
if (result.data && Array.isArray(result.data)) {
|
|
255
|
-
result.data = result.data.map((item, index) => ({
|
|
256
|
-
...item,
|
|
257
|
-
rownumerator: options.start + index + 1
|
|
258
|
-
}));
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Add metadata untuk debugging (development only)
|
|
262
|
-
if (process.env.NODE_ENV === 'development') {
|
|
263
|
-
result._metadata = {
|
|
264
|
-
endpoint: 'item',
|
|
265
|
-
options: options,
|
|
266
|
-
timestamp: new Date().toISOString()
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return res.json(result);
|
|
271
|
-
} catch (error) {
|
|
272
|
-
console.error('Error in item datatables:', error);
|
|
273
|
-
const statusCode = error.statusCode || 500;
|
|
274
|
-
return res.status(statusCode).json({
|
|
275
|
-
success: false,
|
|
276
|
-
error: statusCode === 400 ? 'Bad Request' : 'Internal Server Error',
|
|
277
|
-
message: statusCode === 400 ? error.message : 'An error occurred while fetching item data',
|
|
278
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
279
|
-
timestamp: new Date().toISOString()
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// POST /api/mini-inventory/item/create - Menambahkan data item baru
|
|
286
|
-
router.post('/create', async (req, res) => {
|
|
287
|
-
try {
|
|
288
|
-
// req.log.debug('Request body item/create:', JSON.stringify(req.body, null, 2));
|
|
289
|
-
|
|
290
|
-
// Validasi payload
|
|
291
|
-
if (!req.body || Object.keys(req.body).length === 0) {
|
|
292
|
-
return res.status(400).json({
|
|
293
|
-
success: false,
|
|
294
|
-
error: 'Invalid payload',
|
|
295
|
-
message: 'Payload cannot be empty',
|
|
296
|
-
timestamp: new Date().toISOString()
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Get correlation ID from header (optional)
|
|
301
|
-
const correlationId = req.headers['x-correlation-id'] || null;
|
|
302
|
-
|
|
303
|
-
// Validasi data dengan model jika tersedia
|
|
304
|
-
if (typeof itemModel.validateData === 'function') {
|
|
305
|
-
const validation = await itemModel.validateData(req.body, 'insert');
|
|
306
|
-
if (!validation.isValid) {
|
|
307
|
-
return res.status(400).json({
|
|
308
|
-
success: false,
|
|
309
|
-
error: 'Validation failed',
|
|
310
|
-
message: 'Invalid data',
|
|
311
|
-
errors: validation.errors,
|
|
312
|
-
timestamp: new Date().toISOString()
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Gunakan sanitized data
|
|
317
|
-
req.body = { ...req.body, ...validation.sanitizedData };
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Event lifecycle variables
|
|
321
|
-
let oldData = null;
|
|
322
|
-
let newData = null;
|
|
323
|
-
const requestData = req.body;
|
|
324
|
-
let responseData = null;
|
|
325
|
-
|
|
326
|
-
// === Integrated Transaction dengan Event Lifecycle ===
|
|
327
|
-
// onBefore + main operation + onAfter dieksekusi dalam satu transaction scope
|
|
328
|
-
// di dalam model.executeTransactionWithEvents()
|
|
329
|
-
if (componentEngine && ContextBuilder) {
|
|
330
|
-
// Gunakan integrated transaction model dengan event lifecycle
|
|
331
|
-
try {
|
|
332
|
-
const eventContext = {
|
|
333
|
-
componentEngine: componentEngine,
|
|
334
|
-
ContextBuilder: ContextBuilder,
|
|
335
|
-
tableName: 'core.item',
|
|
336
|
-
additionalContext: {
|
|
337
|
-
user_id: req.headers['user-id'] || req.headers['x-user-id'] || 'system',
|
|
338
|
-
options: req.bodyOptions || {},
|
|
339
|
-
requestId: req.id || null
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
responseData = await itemModel.addData(req.body, eventContext);
|
|
344
|
-
newData = responseData;
|
|
345
|
-
|
|
346
|
-
console.log('[INTEGRATED TRANSACTION] INSERT completed successfully with events');
|
|
347
|
-
} catch (error) {
|
|
348
|
-
console.error('[INTEGRATED TRANSACTION] INSERT failed:', error.message);
|
|
349
|
-
throw error;
|
|
350
|
-
}
|
|
351
|
-
} else {
|
|
352
|
-
// Fallback: gunakan mode lama tanpa events (tetap propagasi requestId untuk Live Sync)
|
|
353
|
-
try {
|
|
354
|
-
responseData = await itemModel.addData(req.body, { additionalContext: { requestId: req.id || null } });
|
|
355
|
-
newData = responseData;
|
|
356
|
-
console.log('[FALLBACK] INSERT completed without events');
|
|
357
|
-
} catch (error) {
|
|
358
|
-
console.error('[FALLBACK] INSERT failed:', error.message);
|
|
359
|
-
throw error;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Log successful operation
|
|
364
|
-
console.log(`item data added successfully: ${responseData.id || 'new record'}`);
|
|
365
|
-
|
|
366
|
-
// Kirim response
|
|
367
|
-
return res.status(201).json({
|
|
368
|
-
success: true,
|
|
369
|
-
message: 'item data successfully added',
|
|
370
|
-
data: responseData,
|
|
371
|
-
timestamp: new Date().toISOString()
|
|
372
|
-
});
|
|
373
|
-
} catch (error) {
|
|
374
|
-
console.error('Error saat menambahkan data item:', error);
|
|
375
|
-
|
|
376
|
-
// Handle specific error types
|
|
377
|
-
if (error.code === '23505') { // PostgreSQL unique violation
|
|
378
|
-
return res.status(409).json({
|
|
379
|
-
success: false,
|
|
380
|
-
error: 'Duplicate entry',
|
|
381
|
-
message: 'A record with this value already exists',
|
|
382
|
-
timestamp: new Date().toISOString()
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (error.code === '23503') { // PostgreSQL foreign key violation
|
|
387
|
-
return res.status(400).json({
|
|
388
|
-
success: false,
|
|
389
|
-
error: 'Foreign key constraint',
|
|
390
|
-
message: 'Referenced data not found',
|
|
391
|
-
timestamp: new Date().toISOString()
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return res.status(500).json({
|
|
396
|
-
success: false,
|
|
397
|
-
error: 'Internal Server Error',
|
|
398
|
-
message: 'An error occurred while adding item data',
|
|
399
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
400
|
-
timestamp: new Date().toISOString()
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
// POST /api/mini-inventory/item/update - Mengupdate data item
|
|
407
|
-
router.post('/update', async (req, res) => {
|
|
408
|
-
try {
|
|
409
|
-
// req.log.debug('Request body item/update:', JSON.stringify(req.body, null, 2));
|
|
410
|
-
|
|
411
|
-
// Validasi payload
|
|
412
|
-
if (!req.body || Object.keys(req.body).length === 0) {
|
|
413
|
-
return res.status(400).json({
|
|
414
|
-
success: false,
|
|
415
|
-
error: 'Invalid payload',
|
|
416
|
-
message: 'Payload cannot be empty',
|
|
417
|
-
timestamp: new Date().toISOString()
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Get correlation ID from header (optional)
|
|
422
|
-
const correlationId = req.headers['x-correlation-id'] || null;
|
|
423
|
-
|
|
424
|
-
// Validasi primary key
|
|
425
|
-
const primaryKey = 'id';
|
|
426
|
-
if (!req.body[primaryKey]) {
|
|
427
|
-
return res.status(400).json({
|
|
428
|
-
success: false,
|
|
429
|
-
error: 'Missing required field',
|
|
430
|
-
message: `Primary key (${primaryKey}) is required for update`,
|
|
431
|
-
timestamp: new Date().toISOString()
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Cek apakah primary key adalah UUID yang valid (jika primary key adalah 'id')
|
|
436
|
-
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
437
|
-
const isValidUUID = primaryKey === 'id' && uuidRegex.test(req.body[primaryKey]);
|
|
438
|
-
|
|
439
|
-
// Validasi data dengan model jika tersedia
|
|
440
|
-
if (typeof itemModel.validateData === 'function') {
|
|
441
|
-
const validation = await itemModel.validateData(req.body, 'update');
|
|
442
|
-
if (!validation.isValid) {
|
|
443
|
-
return res.status(400).json({
|
|
444
|
-
success: false,
|
|
445
|
-
error: 'Validation failed',
|
|
446
|
-
message: 'Invalid data',
|
|
447
|
-
errors: validation.errors,
|
|
448
|
-
timestamp: new Date().toISOString()
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Gunakan sanitized data
|
|
453
|
-
req.body = { ...req.body, ...validation.sanitizedData };
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Event lifecycle variables
|
|
457
|
-
let oldData = null;
|
|
458
|
-
let newData = null;
|
|
459
|
-
const requestData = req.body;
|
|
460
|
-
let responseData = null;
|
|
461
|
-
|
|
462
|
-
// === Integrated Transaction dengan Event Lifecycle ===
|
|
463
|
-
// oldData fetch + onBefore + main operation + onAfter dieksekusi dalam satu transaction scope
|
|
464
|
-
// di dalam model.executeTransactionWithEvents()
|
|
465
|
-
if (componentEngine && ContextBuilder) {
|
|
466
|
-
// Gunakan integrated transaction model dengan event lifecycle
|
|
467
|
-
try {
|
|
468
|
-
const eventContext = {
|
|
469
|
-
componentEngine: componentEngine,
|
|
470
|
-
ContextBuilder: ContextBuilder,
|
|
471
|
-
tableName: 'core.item',
|
|
472
|
-
additionalContext: {
|
|
473
|
-
user_id: req.headers['user-id'] || req.headers['x-user-id'] || 'system',
|
|
474
|
-
options: req.bodyOptions || {},
|
|
475
|
-
requestId: req.id || null
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
responseData = await itemModel.updateData(req.body, eventContext);
|
|
480
|
-
newData = responseData;
|
|
481
|
-
|
|
482
|
-
console.log('[INTEGRATED TRANSACTION] UPDATE completed successfully with events');
|
|
483
|
-
} catch (error) {
|
|
484
|
-
console.error('[INTEGRATED TRANSACTION] UPDATE failed:', error.message);
|
|
485
|
-
throw error;
|
|
486
|
-
}
|
|
487
|
-
} else {
|
|
488
|
-
// Fallback: gunakan mode lama tanpa events (tetap propagasi requestId untuk Live Sync)
|
|
489
|
-
try {
|
|
490
|
-
responseData = await itemModel.updateData(req.body, { additionalContext: { requestId: req.id || null } });
|
|
491
|
-
newData = responseData;
|
|
492
|
-
console.log('[FALLBACK] UPDATE completed without events');
|
|
493
|
-
} catch (error) {
|
|
494
|
-
console.error('[FALLBACK] UPDATE failed:', error.message);
|
|
495
|
-
throw error;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Log successful operation
|
|
500
|
-
console.log(`item data updated successfully: ${primaryKey}=${req.body[primaryKey]}`);
|
|
501
|
-
|
|
502
|
-
// Kirim response
|
|
503
|
-
return res.status(200).json({
|
|
504
|
-
success: true,
|
|
505
|
-
message: 'item data successfully updated',
|
|
506
|
-
data: responseData,
|
|
507
|
-
timestamp: new Date().toISOString()
|
|
508
|
-
});
|
|
509
|
-
} catch (error) {
|
|
510
|
-
console.error('Error saat mengupdate data item:', error);
|
|
511
|
-
|
|
512
|
-
// Handle khusus untuk error "Data tidak ditemukan"
|
|
513
|
-
if (error.message === 'Data tidak ditemukan' || error.message.includes('not found')) {
|
|
514
|
-
return res.status(404).json({
|
|
515
|
-
success: false,
|
|
516
|
-
error: 'Data not found',
|
|
517
|
-
message: 'item data not found',
|
|
518
|
-
timestamp: new Date().toISOString()
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Handle specific database errors
|
|
523
|
-
if (error.code === '23505') { // PostgreSQL unique violation
|
|
524
|
-
return res.status(409).json({
|
|
525
|
-
success: false,
|
|
526
|
-
error: 'Duplicate entry',
|
|
527
|
-
message: 'A record with this value already exists',
|
|
528
|
-
timestamp: new Date().toISOString()
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return res.status(500).json({
|
|
533
|
-
success: false,
|
|
534
|
-
error: 'Internal Server Error',
|
|
535
|
-
message: 'An error occurred while updating item data',
|
|
536
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
537
|
-
timestamp: new Date().toISOString()
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
// POST /api/mini-inventory/item/delete - Menghapus data item
|
|
544
|
-
router.post('/delete', async (req, res) => {
|
|
545
|
-
try {
|
|
546
|
-
// req.log.debug('Request body item/delete:', JSON.stringify(req.body, null, 2));
|
|
547
|
-
|
|
548
|
-
// Validasi request body
|
|
549
|
-
if (!req.body || Object.keys(req.body).length === 0) {
|
|
550
|
-
return res.status(400).json({
|
|
551
|
-
success: false,
|
|
552
|
-
error: 'Invalid payload',
|
|
553
|
-
message: 'Payload cannot be empty',
|
|
554
|
-
timestamp: new Date().toISOString()
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// Get correlation ID from header (optional)
|
|
559
|
-
const correlationId = req.headers['x-correlation-id'] || null;
|
|
560
|
-
|
|
561
|
-
if (!req.body.where) {
|
|
562
|
-
return res.status(400).json({
|
|
563
|
-
success: false,
|
|
564
|
-
error: 'Missing required field',
|
|
565
|
-
message: 'Invalid request format: where parameter is required',
|
|
566
|
-
example: {
|
|
567
|
-
"where": [{ "key": "id", "value": "your-id-value" }]
|
|
568
|
-
},
|
|
569
|
-
timestamp: new Date().toISOString()
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Validasi format where
|
|
574
|
-
if (!Array.isArray(req.body.where) && !req.body.where.conditions) {
|
|
575
|
-
return res.status(400).json({
|
|
576
|
-
success: false,
|
|
577
|
-
error: 'Invalid where format',
|
|
578
|
-
message: 'Invalid where format',
|
|
579
|
-
example: {
|
|
580
|
-
"where": [
|
|
581
|
-
{ "key": "id", "value": "your-id-value" }
|
|
582
|
-
]
|
|
583
|
-
},
|
|
584
|
-
timestamp: new Date().toISOString()
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// === EVENT LIFECYCLE: onBefore DELETE ===
|
|
589
|
-
let oldData = null;
|
|
590
|
-
let newData = null;
|
|
591
|
-
const requestData = req.body;
|
|
592
|
-
let responseData = null;
|
|
593
|
-
|
|
594
|
-
// Cek apakah data exist sebelum delete dan ambil old data untuk event lifecycle
|
|
595
|
-
// Menggunakan SELECT * dari tabel utama (tanpa explicit select) karena fieldName
|
|
596
|
-
// bisa mengandung kolom dari JOIN (mis. city_name) yang tidak ada di tabel utama
|
|
597
|
-
if (req.body.where && Array.isArray(req.body.where) && req.body.where.length > 0) {
|
|
598
|
-
const firstCondition = req.body.where[0];
|
|
599
|
-
try {
|
|
600
|
-
const existingData = await itemModel.getData({
|
|
601
|
-
where: [{ key: firstCondition.key, value: firstCondition.value }]
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
if (!existingData.success || !existingData.data || existingData.data.length === 0) {
|
|
605
|
-
return res.status(404).json({
|
|
606
|
-
success: false,
|
|
607
|
-
error: 'Data not found',
|
|
608
|
-
message: 'item data not found',
|
|
609
|
-
timestamp: new Date().toISOString()
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Simpan data untuk event lifecycle dan debug
|
|
614
|
-
oldData = existingData.data[0];
|
|
615
|
-
|
|
616
|
-
} catch (checkError) {
|
|
617
|
-
return res.status(500).json({
|
|
618
|
-
success: false,
|
|
619
|
-
error: 'Verification Failed',
|
|
620
|
-
message: 'Could not verify data existence before delete',
|
|
621
|
-
details: process.env.NODE_ENV === 'development' ? checkError.message : undefined,
|
|
622
|
-
timestamp: new Date().toISOString()
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// === Integrated Transaction dengan Event Lifecycle ===
|
|
628
|
-
// onBefore + main operation + onAfter dieksekusi dalam satu transaction scope
|
|
629
|
-
// di dalam model.executeTransactionWithEvents()
|
|
630
|
-
if (componentEngine && ContextBuilder) {
|
|
631
|
-
// Gunakan integrated transaction model dengan event lifecycle
|
|
632
|
-
try {
|
|
633
|
-
const eventContext = {
|
|
634
|
-
componentEngine: componentEngine,
|
|
635
|
-
ContextBuilder: ContextBuilder,
|
|
636
|
-
tableName: 'core.item',
|
|
637
|
-
additionalContext: {
|
|
638
|
-
user_id: req.headers['user-id'] || req.headers['x-user-id'] || 'system',
|
|
639
|
-
options: req.bodyOptions || {},
|
|
640
|
-
requestId: req.id || null
|
|
641
|
-
}
|
|
642
|
-
};
|
|
643
|
-
|
|
644
|
-
responseData = await itemModel.deleteData(req.body, eventContext);
|
|
645
|
-
|
|
646
|
-
console.log('[INTEGRATED TRANSACTION] DELETE completed successfully with events');
|
|
647
|
-
} catch (error) {
|
|
648
|
-
console.error('[INTEGRATED TRANSACTION] DELETE failed:', error.message);
|
|
649
|
-
throw error;
|
|
650
|
-
}
|
|
651
|
-
} else {
|
|
652
|
-
// Fallback: gunakan mode lama tanpa events (tetap propagasi requestId untuk Live Sync)
|
|
653
|
-
try {
|
|
654
|
-
responseData = await itemModel.deleteData(req.body, { additionalContext: { requestId: req.id || null } });
|
|
655
|
-
console.log('[FALLBACK] DELETE completed without events');
|
|
656
|
-
} catch (error) {
|
|
657
|
-
console.error('[FALLBACK] DELETE failed:', error.message);
|
|
658
|
-
throw error;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Log successful operation
|
|
663
|
-
console.log(`item data deleted successfully`);
|
|
664
|
-
|
|
665
|
-
return res.json({
|
|
666
|
-
...responseData,
|
|
667
|
-
timestamp: new Date().toISOString()
|
|
668
|
-
});
|
|
669
|
-
} catch (error) {
|
|
670
|
-
console.error('Error saat menghapus data item:', error);
|
|
671
|
-
|
|
672
|
-
// Handle foreign key constraint errors
|
|
673
|
-
if (error.code === '23503') { // PostgreSQL foreign key violation
|
|
674
|
-
return res.status(409).json({
|
|
675
|
-
success: false,
|
|
676
|
-
error: 'Foreign key constraint',
|
|
677
|
-
message: 'Cannot delete: record is still referenced by other data',
|
|
678
|
-
timestamp: new Date().toISOString()
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
return res.status(500).json({
|
|
683
|
-
success: false,
|
|
684
|
-
error: 'Internal Server Error',
|
|
685
|
-
message: 'An error occurred while deleting item data',
|
|
686
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
687
|
-
timestamp: new Date().toISOString()
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
// GET /api/mini-inventory/item/lookup - Pencarian item untuk dropdown (dynamic)
|
|
694
|
-
router.get('/lookup', async (req, res) => {
|
|
695
|
-
try {
|
|
696
|
-
// Validasi X-Request-Mode header
|
|
697
|
-
const requestMode = req.headers['x-request-mode'];
|
|
698
|
-
console.log(`X-Request-Mode header: ${requestMode}`);
|
|
699
|
-
|
|
700
|
-
if (requestMode !== 'dynamic') {
|
|
701
|
-
return res.status(400).json({
|
|
702
|
-
success: false,
|
|
703
|
-
error: 'Invalid Request Mode',
|
|
704
|
-
message: 'X-Request-Mode header must be set to dynamic',
|
|
705
|
-
details: 'Contoh penggunaan: Tambahkan header X-Request-Mode: dynamic',
|
|
706
|
-
timestamp: new Date().toISOString()
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// Ambil parameter search (default case-insensitive)
|
|
711
|
-
let search = req.query.search || '';
|
|
712
|
-
if (Array.isArray(search)) {
|
|
713
|
-
search = search[0] || '';
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// Ambil parameter tambahan untuk filtering
|
|
717
|
-
const companyId = req.query.company_id || req.query.companyId;
|
|
718
|
-
const extraFilters = {};
|
|
719
|
-
|
|
720
|
-
// Collect additional filter parameters
|
|
721
|
-
if (companyId) {
|
|
722
|
-
extraFilters.company_id = companyId;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// Validasi search parameter
|
|
726
|
-
if (search.length > 100) {
|
|
727
|
-
return res.status(400).json({
|
|
728
|
-
success: false,
|
|
729
|
-
error: 'Invalid search parameter',
|
|
730
|
-
message: 'Search parameter terlalu panjang (max 100 karakter)',
|
|
731
|
-
timestamp: new Date().toISOString()
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
console.log(`item lookup request: search=${search}, filters=`, extraFilters);
|
|
736
|
-
|
|
737
|
-
// Gunakan model untuk lookup data dengan filters tambahan
|
|
738
|
-
const list = await itemModel.getLookupDataDynamic(search, extraFilters);
|
|
739
|
-
|
|
740
|
-
console.log(`Lookup results: ${list.length} records found`);
|
|
741
|
-
return res.json({
|
|
742
|
-
success: true,
|
|
743
|
-
count: list.length,
|
|
744
|
-
data: list,
|
|
745
|
-
search: search,
|
|
746
|
-
timestamp: new Date().toISOString()
|
|
747
|
-
});
|
|
748
|
-
} catch (error) {
|
|
749
|
-
console.error('Error in item lookup:', error);
|
|
750
|
-
return res.status(500).json({
|
|
751
|
-
success: false,
|
|
752
|
-
error: 'Internal Server Error',
|
|
753
|
-
message: 'An error occurred while looking up item data',
|
|
754
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
755
|
-
timestamp: new Date().toISOString()
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
// POST /api/mini-inventory/item/lookup - Mendapatkan data item untuk lookup dengan filtering
|
|
761
|
-
router.post('/lookup', async (req, res) => {
|
|
762
|
-
try {
|
|
763
|
-
// Validasi X-Request-Mode header
|
|
764
|
-
const requestMode = req.headers['x-request-mode'];
|
|
765
|
-
console.log(`X-Request-Mode header for POST lookup: ${requestMode}`);
|
|
766
|
-
|
|
767
|
-
if (requestMode !== 'static') {
|
|
768
|
-
return res.status(400).json({
|
|
769
|
-
success: false,
|
|
770
|
-
error: 'Invalid Request Mode',
|
|
771
|
-
message: 'X-Request-Mode header must be set to static for POST method',
|
|
772
|
-
details: 'Contoh penggunaan: Tambahkan header X-Request-Mode: static',
|
|
773
|
-
timestamp: new Date().toISOString()
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// Validasi payload
|
|
778
|
-
if (!req.body) {
|
|
779
|
-
// Support untuk fieldNameLookup config
|
|
780
|
-
const lookupConfig = null;
|
|
781
|
-
const defaultSelect = lookupConfig && lookupConfig.hasCustomText
|
|
782
|
-
? ["id", lookupConfig.textField]
|
|
783
|
-
: ["id", "item_id"];
|
|
784
|
-
|
|
785
|
-
return res.status(400).json({
|
|
786
|
-
success: false,
|
|
787
|
-
error: 'Invalid Payload',
|
|
788
|
-
message: 'Payload cannot be empty',
|
|
789
|
-
example: {
|
|
790
|
-
"selected_tag": "optional-id-if-needed",
|
|
791
|
-
"where": [{ "key": "item_id", "value": "AP2506-01" }],
|
|
792
|
-
"select": defaultSelect
|
|
793
|
-
},
|
|
794
|
-
example_sql_expression: {
|
|
795
|
-
"where": [{ "key": "company_id", "value": "your-company-id" }],
|
|
796
|
-
"select": ["id", "item_id||' - '||code as display_text"]
|
|
797
|
-
},
|
|
798
|
-
example_custom_lookup: lookupConfig ? {
|
|
799
|
-
"where": [{ "key": "company_id", "value": "your-company-id" }],
|
|
800
|
-
"select": [lookupConfig.idField, lookupConfig.textField],
|
|
801
|
-
"note": "Using fieldNameLookup configuration"
|
|
802
|
-
} : null,
|
|
803
|
-
example_advanced: {
|
|
804
|
-
"where": {
|
|
805
|
-
"logic": "OR",
|
|
806
|
-
"conditions": [
|
|
807
|
-
{ "key": "item_id", "operator": "like", "value": "%Portal%" },
|
|
808
|
-
{ "key": "item_id", "operator": "like", "value": "%system%", "sensitive": false }
|
|
809
|
-
]
|
|
810
|
-
},
|
|
811
|
-
"select": ["id", "item_code", "item_name"]
|
|
812
|
-
},
|
|
813
|
-
example_with_ordering: {
|
|
814
|
-
"select": ["id", "item_id"],
|
|
815
|
-
"sort_columns": [
|
|
816
|
-
{
|
|
817
|
-
"column": "item_code",
|
|
818
|
-
"direction": "ASC"
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
"column": "item_id",
|
|
822
|
-
"direction": "ASC"
|
|
823
|
-
}
|
|
824
|
-
]
|
|
825
|
-
},
|
|
826
|
-
timestamp: new Date().toISOString()
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// req.log.debug('Request body item/lookup:', JSON.stringify(req.body, null, 2));
|
|
831
|
-
|
|
832
|
-
// Cek apakah ada where clause (format baru) atau selected_tag (format lama)
|
|
833
|
-
if (req.body.where) {
|
|
834
|
-
// Format baru dengan where clause
|
|
835
|
-
console.log('Using new format with where clause');
|
|
836
|
-
|
|
837
|
-
// Validasi format where
|
|
838
|
-
if (!Array.isArray(req.body.where) && !req.body.where.conditions) {
|
|
839
|
-
return res.status(400).json({
|
|
840
|
-
success: false,
|
|
841
|
-
error: 'Invalid where format',
|
|
842
|
-
message: 'Invalid where format',
|
|
843
|
-
example: {
|
|
844
|
-
"where": [
|
|
845
|
-
{ "key": "item_id", "value": "AP2506-01" }
|
|
846
|
-
],
|
|
847
|
-
"select": ["id", "item_id"]
|
|
848
|
-
},
|
|
849
|
-
example_advanced: {
|
|
850
|
-
"where": {
|
|
851
|
-
"logic": "OR",
|
|
852
|
-
"conditions": [
|
|
853
|
-
{ "key": "item_id", "operator": "like", "value": "%Portal%" },
|
|
854
|
-
{ "key": "item_id", "operator": "like", "value": "%system%", "sensitive": false }
|
|
855
|
-
]
|
|
856
|
-
},
|
|
857
|
-
"select": ["id", "item_code", "item_name"]
|
|
858
|
-
},
|
|
859
|
-
timestamp: new Date().toISOString()
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// Validasi select fields jika ada (support SQL expressions)
|
|
864
|
-
if (req.body.select && Array.isArray(req.body.select)) {
|
|
865
|
-
const validFields = ["item_id","item_code","item_name","description","uom","unit_price","weight","is_active","created_at","created_by","updated_at","updated_by"];
|
|
866
|
-
const invalidFields = [];
|
|
867
|
-
|
|
868
|
-
// Check setiap field dalam select
|
|
869
|
-
for (const field of req.body.select) {
|
|
870
|
-
// Skip validasi untuk field 'id'
|
|
871
|
-
if (field.toLowerCase() === 'id') {
|
|
872
|
-
continue;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
// Skip validasi jika ada SQL expression dengan AS (alias)
|
|
876
|
-
if (/\s+as\s+\w+$/i.test(field)) {
|
|
877
|
-
continue;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Skip validasi jika ada SQL operators/functions (||, CONCAT, dll)
|
|
881
|
-
if (/\|\||CONCAT|COALESCE|CASE|WHEN/i.test(field)) {
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// Validasi normal field name
|
|
886
|
-
if (!validFields.includes(field)) {
|
|
887
|
-
invalidFields.push(field);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
if (invalidFields.length > 0) {
|
|
892
|
-
return res.status(400).json({
|
|
893
|
-
success: false,
|
|
894
|
-
error: 'Invalid select fields',
|
|
895
|
-
message: `Invalid field(s): ${invalidFields.join(', ')}`,
|
|
896
|
-
validFields: validFields,
|
|
897
|
-
sqlExpressionNote: 'SQL expressions dengan operator || atau AS alias diperbolehkan',
|
|
898
|
-
timestamp: new Date().toISOString()
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Gunakan method getLookupDataWithFilter
|
|
904
|
-
const list = await itemModel.getLookupDataWithFilter(req.body);
|
|
905
|
-
|
|
906
|
-
console.log(`Lookup with filter results: ${list.length} records found`);
|
|
907
|
-
return res.json({
|
|
908
|
-
success: true,
|
|
909
|
-
count: list.length,
|
|
910
|
-
data: list,
|
|
911
|
-
query: req.body,
|
|
912
|
-
timestamp: new Date().toISOString()
|
|
913
|
-
});
|
|
914
|
-
|
|
915
|
-
} else {
|
|
916
|
-
// Format lama atau request tanpa WHERE clause tetapi dengan SELECT
|
|
917
|
-
console.log('Using legacy format with selected_tag or select-only request');
|
|
918
|
-
|
|
919
|
-
const selectedTag = req.body.selected_tag || '';
|
|
920
|
-
console.log(`item static lookup request with selected_tag: ${selectedTag}`);
|
|
921
|
-
|
|
922
|
-
// Jika ada field select atau sort_columns, gunakan getLookupDataWithFilter dengan body yang dimodifikasi
|
|
923
|
-
if ((req.body.select && Array.isArray(req.body.select)) || (req.body.sort_columns && Array.isArray(req.body.sort_columns))) {
|
|
924
|
-
console.log('Found select fields or sort_columns, using getLookupDataWithFilter');
|
|
925
|
-
|
|
926
|
-
// Buat body baru dengan where kosong untuk mengambil semua data
|
|
927
|
-
const modifiedBody = {
|
|
928
|
-
...req.body,
|
|
929
|
-
where: [] // Empty where untuk mengambil semua data
|
|
930
|
-
};
|
|
931
|
-
|
|
932
|
-
const list = await itemModel.getLookupDataWithFilter(modifiedBody);
|
|
933
|
-
|
|
934
|
-
console.log(`Static lookup with select/order results: ${list.length} records found`);
|
|
935
|
-
return res.json({
|
|
936
|
-
success: true,
|
|
937
|
-
count: list.length,
|
|
938
|
-
data: list,
|
|
939
|
-
selectedTag: selectedTag,
|
|
940
|
-
query: modifiedBody,
|
|
941
|
-
timestamp: new Date().toISOString()
|
|
942
|
-
});
|
|
943
|
-
} else {
|
|
944
|
-
// Gunakan model untuk mendapatkan data dengan method lama
|
|
945
|
-
const list = await itemModel.getStaticLookupData(selectedTag);
|
|
946
|
-
|
|
947
|
-
console.log(`Static lookup results: ${list.length} records found`);
|
|
948
|
-
return res.json({
|
|
949
|
-
success: true,
|
|
950
|
-
count: list.length,
|
|
951
|
-
data: list,
|
|
952
|
-
selectedTag: selectedTag,
|
|
953
|
-
timestamp: new Date().toISOString()
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
} catch (error) {
|
|
959
|
-
console.error('Error in item static lookup:', error);
|
|
960
|
-
return res.status(500).json({
|
|
961
|
-
success: false,
|
|
962
|
-
error: 'Internal Server Error',
|
|
963
|
-
message: 'An error occurred while fetching item data',
|
|
964
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
965
|
-
timestamp: new Date().toISOString()
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
// POST /api/mini-inventory/item/read - Manual pagination endpoint
|
|
972
|
-
router.post('/read', async (req, res) => {
|
|
973
|
-
try {
|
|
974
|
-
// req.log.debug('Request body item/read:', JSON.stringify(req.body, null, 2));
|
|
975
|
-
|
|
976
|
-
// Validasi request body
|
|
977
|
-
if (!req.body || Object.keys(req.body).length === 0) {
|
|
978
|
-
return res.status(400).json({
|
|
979
|
-
success: false,
|
|
980
|
-
error: 'Invalid payload',
|
|
981
|
-
message: 'Payload cannot be empty',
|
|
982
|
-
timestamp: new Date().toISOString()
|
|
983
|
-
});
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// Deteksi mode: paginasi (page dikirim) atau non-paginasi (page tidak dikirim)
|
|
987
|
-
const paginate = req.body.page !== undefined;
|
|
988
|
-
const page = paginate ? parseInt(req.body.page, 10) : null;
|
|
989
|
-
const perPage = paginate ? parseInt(req.body.per_page || 10, 10) : null;
|
|
990
|
-
const limit = !paginate ? Math.min(Math.max(parseInt(req.body.limit || 1000, 10), 1), 5000) : null;
|
|
991
|
-
const searchValue = req.body.search_value || '';
|
|
992
|
-
const searchBy = req.body.search_by || 'item_id';
|
|
993
|
-
|
|
994
|
-
// Parse sort_columns
|
|
995
|
-
let sort_columns = [];
|
|
996
|
-
if (req.body.sort_columns && Array.isArray(req.body.sort_columns) && req.body.sort_columns.length > 0) {
|
|
997
|
-
sort_columns = req.body.sort_columns.map(item => ({
|
|
998
|
-
column: item.column,
|
|
999
|
-
direction: (item.direction || 'ASC').toUpperCase()
|
|
1000
|
-
}));
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
// Proses parameter where dengan format advanced conditions
|
|
1004
|
-
let where = null;
|
|
1005
|
-
if (req.body.where && typeof req.body.where === 'object') {
|
|
1006
|
-
if (Array.isArray(req.body.where) || (req.body.where.conditions && Array.isArray(req.body.where.conditions))) {
|
|
1007
|
-
where = req.body.where;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// Proses parameter select untuk kolom selektif
|
|
1012
|
-
const validFields = ["item_id","item_code","item_name","description","uom","unit_price","weight","is_active","created_at","created_by","updated_at","updated_by"];
|
|
1013
|
-
let select = null;
|
|
1014
|
-
if (req.body.select && Array.isArray(req.body.select)) {
|
|
1015
|
-
const invalidFields = req.body.select.filter(field => !validFields.includes(field));
|
|
1016
|
-
if (invalidFields.length > 0) {
|
|
1017
|
-
return res.status(400).json({
|
|
1018
|
-
success: false,
|
|
1019
|
-
error: 'Invalid select fields',
|
|
1020
|
-
message: `Invalid field(s): ${invalidFields.join(', ')}`,
|
|
1021
|
-
validFields: validFields,
|
|
1022
|
-
timestamp: new Date().toISOString()
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
select = req.body.select;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
// Validasi parameter paginasi (hanya jika mode paginasi)
|
|
1029
|
-
if (paginate) {
|
|
1030
|
-
if (page < 1) {
|
|
1031
|
-
return res.status(400).json({
|
|
1032
|
-
success: false,
|
|
1033
|
-
message: 'Validation failed',
|
|
1034
|
-
errors: {
|
|
1035
|
-
page: ['Page must be greater than 0']
|
|
1036
|
-
}
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
if (perPage < 1 || perPage > 100) {
|
|
1041
|
-
return res.status(400).json({
|
|
1042
|
-
success: false,
|
|
1043
|
-
message: 'Validation failed',
|
|
1044
|
-
errors: {
|
|
1045
|
-
per_page: ['Per page must be between 1 and 100']
|
|
1046
|
-
}
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
if (searchValue.length > 255) {
|
|
1052
|
-
return res.status(400).json({
|
|
1053
|
-
success: false,
|
|
1054
|
-
message: 'Validation failed',
|
|
1055
|
-
errors: {
|
|
1056
|
-
search_value: ['Search value must not exceed 255 characters']
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// Validasi kolom searching
|
|
1062
|
-
if (!validFields.includes(searchBy)) {
|
|
1063
|
-
return res.status(400).json({
|
|
1064
|
-
success: false,
|
|
1065
|
-
message: 'Validation failed',
|
|
1066
|
-
errors: {
|
|
1067
|
-
search_by: [`Invalid search field. Valid fields: ${validFields.join(', ')}`]
|
|
1068
|
-
}
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
// Build options untuk model
|
|
1073
|
-
const options = {
|
|
1074
|
-
searchValue: searchValue,
|
|
1075
|
-
searchBy: searchBy,
|
|
1076
|
-
sort_columns: sort_columns,
|
|
1077
|
-
where: where,
|
|
1078
|
-
select: select
|
|
1079
|
-
};
|
|
1080
|
-
|
|
1081
|
-
if (paginate) {
|
|
1082
|
-
options.page = page;
|
|
1083
|
-
options.perPage = perPage;
|
|
1084
|
-
options.offset = (page - 1) * perPage;
|
|
1085
|
-
} else {
|
|
1086
|
-
options.limit = limit;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
// Gunakan model untuk mendapatkan data list
|
|
1090
|
-
const result = await itemModel.getList(options);
|
|
1091
|
-
|
|
1092
|
-
// Format response berdasarkan mode
|
|
1093
|
-
if (paginate) {
|
|
1094
|
-
const totalPages = Math.ceil(result.totalRecords / perPage);
|
|
1095
|
-
const hasNext = page < totalPages;
|
|
1096
|
-
const hasPrevious = page > 1;
|
|
1097
|
-
|
|
1098
|
-
return res.json({
|
|
1099
|
-
success: true,
|
|
1100
|
-
data: result.data || [],
|
|
1101
|
-
count: result.data ? result.data.length : 0,
|
|
1102
|
-
pagination: {
|
|
1103
|
-
current_page: page,
|
|
1104
|
-
per_page: perPage,
|
|
1105
|
-
total_records: result.totalRecords || 0,
|
|
1106
|
-
total_pages: totalPages,
|
|
1107
|
-
has_next: hasNext,
|
|
1108
|
-
has_previous: hasPrevious
|
|
1109
|
-
}
|
|
1110
|
-
});
|
|
1111
|
-
} else {
|
|
1112
|
-
return res.json({
|
|
1113
|
-
success: true,
|
|
1114
|
-
data: result.data || [],
|
|
1115
|
-
count: result.data ? result.data.length : 0
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
} catch (error) {
|
|
1120
|
-
console.error('Error in item list:', error);
|
|
1121
|
-
const statusCode = error.statusCode || 500;
|
|
1122
|
-
return res.status(statusCode).json({
|
|
1123
|
-
success: false,
|
|
1124
|
-
error: statusCode === 400 ? 'Bad Request' : 'Internal Server Error',
|
|
1125
|
-
message: statusCode === 400 ? error.message : 'An error occurred while fetching item list data',
|
|
1126
|
-
details: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
1127
|
-
timestamp: new Date().toISOString()
|
|
1128
|
-
});
|
|
1129
|
-
}
|
|
1130
|
-
});
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
// Endpoint info — self-documenting API
|
|
1135
|
-
router.get('/info', async (req, res) => {
|
|
1136
|
-
try {
|
|
1137
|
-
const actions = {"datatables":true,"read":true,"first":false,"create":true,"update":true,"delete":true,"lookup":true,"export":false,"import":false,"workflow":false,"info":true};
|
|
1138
|
-
|
|
1139
|
-
const modelInfo = await itemModel.getModelInfo(actions);
|
|
1140
|
-
|
|
1141
|
-
res.json({
|
|
1142
|
-
success: true,
|
|
1143
|
-
endpoint: 'item',
|
|
1144
|
-
module: 'mini-inventory',
|
|
1145
|
-
table: modelInfo.table,
|
|
1146
|
-
fields: modelInfo.fields,
|
|
1147
|
-
querySources: modelInfo.querySources,
|
|
1148
|
-
actions: actions,
|
|
1149
|
-
databaseType: 'postgres',
|
|
1150
|
-
generated: '2026-04-25 07:34:33',
|
|
1151
|
-
timestamp: new Date().toISOString()
|
|
1152
|
-
});
|
|
1153
|
-
} catch (error) {
|
|
1154
|
-
console.error('Error in item info:', error);
|
|
1155
|
-
res.status(500).json({
|
|
1156
|
-
success: false,
|
|
1157
|
-
error: 'Internal Server Error',
|
|
1158
|
-
message: 'An error occurred while fetching endpoint information',
|
|
1159
|
-
timestamp: new Date().toISOString()
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1162
|
-
});
|
|
1163
|
-
|
|
1164
|
-
// Health check endpoint
|
|
1165
|
-
router.get('/health', (req, res) => {
|
|
1166
|
-
res.json({
|
|
1167
|
-
status: 'ok',
|
|
1168
|
-
endpoint: 'item',
|
|
1169
|
-
timestamp: new Date().toISOString()
|
|
1170
|
-
});
|
|
1171
|
-
});
|
|
1172
|
-
|
|
1173
|
-
module.exports = router;
|