@shohaghinfo/aerojs 0.1.0

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.
Files changed (650) hide show
  1. package/AEROJS.md +12 -0
  2. package/AGENTS.md +709 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1186 -0
  5. package/bin/aero.js +24 -0
  6. package/dist/application.d.ts +251 -0
  7. package/dist/application.d.ts.map +1 -0
  8. package/dist/application.js +658 -0
  9. package/dist/application.js.map +1 -0
  10. package/dist/audit/audit-trail.d.ts +122 -0
  11. package/dist/audit/audit-trail.d.ts.map +1 -0
  12. package/dist/audit/audit-trail.js +154 -0
  13. package/dist/audit/audit-trail.js.map +1 -0
  14. package/dist/auth/auth-manager.d.ts +95 -0
  15. package/dist/auth/auth-manager.d.ts.map +1 -0
  16. package/dist/auth/auth-manager.js +130 -0
  17. package/dist/auth/auth-manager.js.map +1 -0
  18. package/dist/auth/bouncer.d.ts +143 -0
  19. package/dist/auth/bouncer.d.ts.map +1 -0
  20. package/dist/auth/bouncer.js +243 -0
  21. package/dist/auth/bouncer.js.map +1 -0
  22. package/dist/auth/guards/api-token-guard.d.ts +68 -0
  23. package/dist/auth/guards/api-token-guard.d.ts.map +1 -0
  24. package/dist/auth/guards/api-token-guard.js +107 -0
  25. package/dist/auth/guards/api-token-guard.js.map +1 -0
  26. package/dist/auth/guards/guard.d.ts +35 -0
  27. package/dist/auth/guards/guard.d.ts.map +1 -0
  28. package/dist/auth/guards/guard.js +7 -0
  29. package/dist/auth/guards/guard.js.map +1 -0
  30. package/dist/auth/guards/jwt-guard.d.ts +64 -0
  31. package/dist/auth/guards/jwt-guard.d.ts.map +1 -0
  32. package/dist/auth/guards/jwt-guard.js +104 -0
  33. package/dist/auth/guards/jwt-guard.js.map +1 -0
  34. package/dist/auth/guards/session-guard.d.ts +57 -0
  35. package/dist/auth/guards/session-guard.d.ts.map +1 -0
  36. package/dist/auth/guards/session-guard.js +101 -0
  37. package/dist/auth/guards/session-guard.js.map +1 -0
  38. package/dist/auth/index.d.ts +2 -0
  39. package/dist/auth/index.d.ts.map +1 -0
  40. package/dist/auth/index.js +2 -0
  41. package/dist/auth/index.js.map +1 -0
  42. package/dist/auth/policy.d.ts +20 -0
  43. package/dist/auth/policy.d.ts.map +1 -0
  44. package/dist/auth/policy.js +49 -0
  45. package/dist/auth/policy.js.map +1 -0
  46. package/dist/body-parser.d.ts +40 -0
  47. package/dist/body-parser.d.ts.map +1 -0
  48. package/dist/body-parser.js +99 -0
  49. package/dist/body-parser.js.map +1 -0
  50. package/dist/cache/cache-driver.d.ts +43 -0
  51. package/dist/cache/cache-driver.d.ts.map +1 -0
  52. package/dist/cache/cache-driver.js +6 -0
  53. package/dist/cache/cache-driver.js.map +1 -0
  54. package/dist/cache/cache-manager.d.ts +44 -0
  55. package/dist/cache/cache-manager.d.ts.map +1 -0
  56. package/dist/cache/cache-manager.js +160 -0
  57. package/dist/cache/cache-manager.js.map +1 -0
  58. package/dist/cache/drivers/database-cache-driver.d.ts +18 -0
  59. package/dist/cache/drivers/database-cache-driver.d.ts.map +1 -0
  60. package/dist/cache/drivers/database-cache-driver.js +77 -0
  61. package/dist/cache/drivers/database-cache-driver.js.map +1 -0
  62. package/dist/cache/drivers/file-cache-driver.d.ts +21 -0
  63. package/dist/cache/drivers/file-cache-driver.d.ts.map +1 -0
  64. package/dist/cache/drivers/file-cache-driver.js +95 -0
  65. package/dist/cache/drivers/file-cache-driver.js.map +1 -0
  66. package/dist/cache/drivers/memory-cache-driver.d.ts +21 -0
  67. package/dist/cache/drivers/memory-cache-driver.d.ts.map +1 -0
  68. package/dist/cache/drivers/memory-cache-driver.js +82 -0
  69. package/dist/cache/drivers/memory-cache-driver.js.map +1 -0
  70. package/dist/cache/drivers/redis-cache-driver.d.ts +18 -0
  71. package/dist/cache/drivers/redis-cache-driver.d.ts.map +1 -0
  72. package/dist/cache/drivers/redis-cache-driver.js +95 -0
  73. package/dist/cache/drivers/redis-cache-driver.js.map +1 -0
  74. package/dist/cache/index.d.ts +7 -0
  75. package/dist/cache/index.d.ts.map +1 -0
  76. package/dist/cache/index.js +7 -0
  77. package/dist/cache/index.js.map +1 -0
  78. package/dist/cli/commands/init.d.ts +10 -0
  79. package/dist/cli/commands/init.d.ts.map +1 -0
  80. package/dist/cli/commands/init.js +471 -0
  81. package/dist/cli/commands/init.js.map +1 -0
  82. package/dist/cli/commands/make.d.ts +16 -0
  83. package/dist/cli/commands/make.d.ts.map +1 -0
  84. package/dist/cli/commands/make.js +157 -0
  85. package/dist/cli/commands/make.js.map +1 -0
  86. package/dist/cli/commands/new.d.ts +7 -0
  87. package/dist/cli/commands/new.d.ts.map +1 -0
  88. package/dist/cli/commands/new.js +175 -0
  89. package/dist/cli/commands/new.js.map +1 -0
  90. package/dist/cli/commands/vault.d.ts +7 -0
  91. package/dist/cli/commands/vault.d.ts.map +1 -0
  92. package/dist/cli/commands/vault.js +39 -0
  93. package/dist/cli/commands/vault.js.map +1 -0
  94. package/dist/cli/index.d.ts +8 -0
  95. package/dist/cli/index.d.ts.map +1 -0
  96. package/dist/cli/index.js +8 -0
  97. package/dist/cli/index.js.map +1 -0
  98. package/dist/cli/runner.d.ts +10 -0
  99. package/dist/cli/runner.d.ts.map +1 -0
  100. package/dist/cli/runner.js +207 -0
  101. package/dist/cli/runner.js.map +1 -0
  102. package/dist/cli/templates/ecommerce-erp.d.ts +6 -0
  103. package/dist/cli/templates/ecommerce-erp.d.ts.map +1 -0
  104. package/dist/cli/templates/ecommerce-erp.js +1760 -0
  105. package/dist/cli/templates/ecommerce-erp.js.map +1 -0
  106. package/dist/cli/templates/fullstack-templates.d.ts +17 -0
  107. package/dist/cli/templates/fullstack-templates.d.ts.map +1 -0
  108. package/dist/cli/templates/fullstack-templates.js +17 -0
  109. package/dist/cli/templates/fullstack-templates.js.map +1 -0
  110. package/dist/config/config.d.ts +25 -0
  111. package/dist/config/config.d.ts.map +1 -0
  112. package/dist/config/config.js +53 -0
  113. package/dist/config/config.js.map +1 -0
  114. package/dist/config/env-schema.d.ts +62 -0
  115. package/dist/config/env-schema.d.ts.map +1 -0
  116. package/dist/config/env-schema.js +203 -0
  117. package/dist/config/env-schema.js.map +1 -0
  118. package/dist/config/env.d.ts +31 -0
  119. package/dist/config/env.d.ts.map +1 -0
  120. package/dist/config/env.js +153 -0
  121. package/dist/config/env.js.map +1 -0
  122. package/dist/context.d.ts +181 -0
  123. package/dist/context.d.ts.map +1 -0
  124. package/dist/context.js +277 -0
  125. package/dist/context.js.map +1 -0
  126. package/dist/core/application-core.d.ts +79 -0
  127. package/dist/core/application-core.d.ts.map +1 -0
  128. package/dist/core/application-core.js +560 -0
  129. package/dist/core/application-core.js.map +1 -0
  130. package/dist/core/application.d.ts +121 -0
  131. package/dist/core/application.d.ts.map +1 -0
  132. package/dist/core/application.js +179 -0
  133. package/dist/core/application.js.map +1 -0
  134. package/dist/core/body-parser.d.ts +12 -0
  135. package/dist/core/body-parser.d.ts.map +1 -0
  136. package/dist/core/body-parser.js +85 -0
  137. package/dist/core/body-parser.js.map +1 -0
  138. package/dist/core/context.d.ts +94 -0
  139. package/dist/core/context.d.ts.map +1 -0
  140. package/dist/core/context.js +278 -0
  141. package/dist/core/context.js.map +1 -0
  142. package/dist/core/error-dashboard.d.ts +35 -0
  143. package/dist/core/error-dashboard.d.ts.map +1 -0
  144. package/dist/core/error-dashboard.js +481 -0
  145. package/dist/core/error-dashboard.js.map +1 -0
  146. package/dist/core/errors.d.ts +83 -0
  147. package/dist/core/errors.d.ts.map +1 -0
  148. package/dist/core/errors.js +117 -0
  149. package/dist/core/errors.js.map +1 -0
  150. package/dist/core/hooks.d.ts +39 -0
  151. package/dist/core/hooks.d.ts.map +1 -0
  152. package/dist/core/hooks.js +65 -0
  153. package/dist/core/hooks.js.map +1 -0
  154. package/dist/core/middleware.d.ts +15 -0
  155. package/dist/core/middleware.d.ts.map +1 -0
  156. package/dist/core/middleware.js +45 -0
  157. package/dist/core/middleware.js.map +1 -0
  158. package/dist/core/request.d.ts +50 -0
  159. package/dist/core/request.d.ts.map +1 -0
  160. package/dist/core/request.js +94 -0
  161. package/dist/core/request.js.map +1 -0
  162. package/dist/core/response.d.ts +49 -0
  163. package/dist/core/response.d.ts.map +1 -0
  164. package/dist/core/response.js +302 -0
  165. package/dist/core/response.js.map +1 -0
  166. package/dist/core/router.d.ts +58 -0
  167. package/dist/core/router.d.ts.map +1 -0
  168. package/dist/core/router.js +298 -0
  169. package/dist/core/router.js.map +1 -0
  170. package/dist/core/types.d.ts +70 -0
  171. package/dist/core/types.d.ts.map +1 -0
  172. package/dist/core/types.js +6 -0
  173. package/dist/core/types.js.map +1 -0
  174. package/dist/core/utils.d.ts +36 -0
  175. package/dist/core/utils.d.ts.map +1 -0
  176. package/dist/core/utils.js +108 -0
  177. package/dist/core/utils.js.map +1 -0
  178. package/dist/database/connection.d.ts +97 -0
  179. package/dist/database/connection.d.ts.map +1 -0
  180. package/dist/database/connection.js +251 -0
  181. package/dist/database/connection.js.map +1 -0
  182. package/dist/database/drizzle.d.ts +10 -0
  183. package/dist/database/drizzle.d.ts.map +1 -0
  184. package/dist/database/drizzle.js +13 -0
  185. package/dist/database/drizzle.js.map +1 -0
  186. package/dist/database/factory.d.ts +39 -0
  187. package/dist/database/factory.d.ts.map +1 -0
  188. package/dist/database/factory.js +74 -0
  189. package/dist/database/factory.js.map +1 -0
  190. package/dist/database/index.d.ts +16 -0
  191. package/dist/database/index.d.ts.map +1 -0
  192. package/dist/database/index.js +16 -0
  193. package/dist/database/index.js.map +1 -0
  194. package/dist/database/knex.d.ts +33 -0
  195. package/dist/database/knex.d.ts.map +1 -0
  196. package/dist/database/knex.js +123 -0
  197. package/dist/database/knex.js.map +1 -0
  198. package/dist/database/migrator.d.ts +29 -0
  199. package/dist/database/migrator.d.ts.map +1 -0
  200. package/dist/database/migrator.js +83 -0
  201. package/dist/database/migrator.js.map +1 -0
  202. package/dist/database/model-hooks.d.ts +61 -0
  203. package/dist/database/model-hooks.d.ts.map +1 -0
  204. package/dist/database/model-hooks.js +126 -0
  205. package/dist/database/model-hooks.js.map +1 -0
  206. package/dist/database/model.d.ts +279 -0
  207. package/dist/database/model.d.ts.map +1 -0
  208. package/dist/database/model.js +960 -0
  209. package/dist/database/model.js.map +1 -0
  210. package/dist/database/prisma.d.ts +13 -0
  211. package/dist/database/prisma.d.ts.map +1 -0
  212. package/dist/database/prisma.js +28 -0
  213. package/dist/database/prisma.js.map +1 -0
  214. package/dist/database/query-builder.d.ts +178 -0
  215. package/dist/database/query-builder.d.ts.map +1 -0
  216. package/dist/database/query-builder.js +607 -0
  217. package/dist/database/query-builder.js.map +1 -0
  218. package/dist/database/schema.d.ts +38 -0
  219. package/dist/database/schema.d.ts.map +1 -0
  220. package/dist/database/schema.js +134 -0
  221. package/dist/database/schema.js.map +1 -0
  222. package/dist/database/seeder.d.ts +24 -0
  223. package/dist/database/seeder.d.ts.map +1 -0
  224. package/dist/database/seeder.js +27 -0
  225. package/dist/database/seeder.js.map +1 -0
  226. package/dist/di/container.d.ts +52 -0
  227. package/dist/di/container.d.ts.map +1 -0
  228. package/dist/di/container.js +124 -0
  229. package/dist/di/container.js.map +1 -0
  230. package/dist/di/decorators.d.ts +17 -0
  231. package/dist/di/decorators.d.ts.map +1 -0
  232. package/dist/di/decorators.js +63 -0
  233. package/dist/di/decorators.js.map +1 -0
  234. package/dist/di/inject.d.ts +11 -0
  235. package/dist/di/inject.d.ts.map +1 -0
  236. package/dist/di/inject.js +30 -0
  237. package/dist/di/inject.js.map +1 -0
  238. package/dist/di/provider.d.ts +32 -0
  239. package/dist/di/provider.d.ts.map +1 -0
  240. package/dist/di/provider.js +30 -0
  241. package/dist/di/provider.js.map +1 -0
  242. package/dist/diagnostics/diagnostics-plugin.d.ts +9 -0
  243. package/dist/diagnostics/diagnostics-plugin.d.ts.map +1 -0
  244. package/dist/diagnostics/diagnostics-plugin.js +49 -0
  245. package/dist/diagnostics/diagnostics-plugin.js.map +1 -0
  246. package/dist/diagnostics/health-check.d.ts +51 -0
  247. package/dist/diagnostics/health-check.d.ts.map +1 -0
  248. package/dist/diagnostics/health-check.js +122 -0
  249. package/dist/diagnostics/health-check.js.map +1 -0
  250. package/dist/diagnostics/index.d.ts +4 -0
  251. package/dist/diagnostics/index.d.ts.map +1 -0
  252. package/dist/diagnostics/index.js +4 -0
  253. package/dist/diagnostics/index.js.map +1 -0
  254. package/dist/diagnostics/prometheus.d.ts +19 -0
  255. package/dist/diagnostics/prometheus.d.ts.map +1 -0
  256. package/dist/diagnostics/prometheus.js +99 -0
  257. package/dist/diagnostics/prometheus.js.map +1 -0
  258. package/dist/errors.d.ts +139 -0
  259. package/dist/errors.d.ts.map +1 -0
  260. package/dist/errors.js +173 -0
  261. package/dist/errors.js.map +1 -0
  262. package/dist/events/event-bus.d.ts +178 -0
  263. package/dist/events/event-bus.d.ts.map +1 -0
  264. package/dist/events/event-bus.js +182 -0
  265. package/dist/events/event-bus.js.map +1 -0
  266. package/dist/graphql/adapter.d.ts +48 -0
  267. package/dist/graphql/adapter.d.ts.map +1 -0
  268. package/dist/graphql/adapter.js +109 -0
  269. package/dist/graphql/adapter.js.map +1 -0
  270. package/dist/graphql/executor.d.ts +24 -0
  271. package/dist/graphql/executor.d.ts.map +1 -0
  272. package/dist/graphql/executor.js +124 -0
  273. package/dist/graphql/executor.js.map +1 -0
  274. package/dist/graphql/index.d.ts +8 -0
  275. package/dist/graphql/index.d.ts.map +1 -0
  276. package/dist/graphql/index.js +8 -0
  277. package/dist/graphql/index.js.map +1 -0
  278. package/dist/graphql/lexer.d.ts +52 -0
  279. package/dist/graphql/lexer.d.ts.map +1 -0
  280. package/dist/graphql/lexer.js +188 -0
  281. package/dist/graphql/lexer.js.map +1 -0
  282. package/dist/graphql/parser.d.ts +29 -0
  283. package/dist/graphql/parser.d.ts.map +1 -0
  284. package/dist/graphql/parser.js +206 -0
  285. package/dist/graphql/parser.js.map +1 -0
  286. package/dist/graphql/plugin.d.ts +13 -0
  287. package/dist/graphql/plugin.d.ts.map +1 -0
  288. package/dist/graphql/plugin.js +103 -0
  289. package/dist/graphql/plugin.js.map +1 -0
  290. package/dist/graphql/schema.d.ts +15 -0
  291. package/dist/graphql/schema.d.ts.map +1 -0
  292. package/dist/graphql/schema.js +17 -0
  293. package/dist/graphql/schema.js.map +1 -0
  294. package/dist/graphql/types.d.ts +65 -0
  295. package/dist/graphql/types.d.ts.map +1 -0
  296. package/dist/graphql/types.js +74 -0
  297. package/dist/graphql/types.js.map +1 -0
  298. package/dist/hooks.d.ts +67 -0
  299. package/dist/hooks.d.ts.map +1 -0
  300. package/dist/hooks.js +115 -0
  301. package/dist/hooks.js.map +1 -0
  302. package/dist/i18n/i18n.d.ts +42 -0
  303. package/dist/i18n/i18n.d.ts.map +1 -0
  304. package/dist/i18n/i18n.js +158 -0
  305. package/dist/i18n/i18n.js.map +1 -0
  306. package/dist/i18n/index.d.ts +2 -0
  307. package/dist/i18n/index.d.ts.map +1 -0
  308. package/dist/i18n/index.js +2 -0
  309. package/dist/i18n/index.js.map +1 -0
  310. package/dist/index.d.ts +75 -0
  311. package/dist/index.d.ts.map +1 -0
  312. package/dist/index.js +87 -0
  313. package/dist/index.js.map +1 -0
  314. package/dist/inertia/inertia.d.ts +62 -0
  315. package/dist/inertia/inertia.d.ts.map +1 -0
  316. package/dist/inertia/inertia.js +159 -0
  317. package/dist/inertia/inertia.js.map +1 -0
  318. package/dist/logging/index.d.ts +7 -0
  319. package/dist/logging/index.d.ts.map +1 -0
  320. package/dist/logging/index.js +7 -0
  321. package/dist/logging/index.js.map +1 -0
  322. package/dist/logging/logger.d.ts +62 -0
  323. package/dist/logging/logger.d.ts.map +1 -0
  324. package/dist/logging/logger.js +197 -0
  325. package/dist/logging/logger.js.map +1 -0
  326. package/dist/logging/request-context.d.ts +29 -0
  327. package/dist/logging/request-context.d.ts.map +1 -0
  328. package/dist/logging/request-context.js +29 -0
  329. package/dist/logging/request-context.js.map +1 -0
  330. package/dist/mail/drivers/log-mail-driver.d.ts +11 -0
  331. package/dist/mail/drivers/log-mail-driver.d.ts.map +1 -0
  332. package/dist/mail/drivers/log-mail-driver.js +23 -0
  333. package/dist/mail/drivers/log-mail-driver.js.map +1 -0
  334. package/dist/mail/drivers/mail-driver.d.ts +14 -0
  335. package/dist/mail/drivers/mail-driver.d.ts.map +1 -0
  336. package/dist/mail/drivers/mail-driver.js +6 -0
  337. package/dist/mail/drivers/mail-driver.js.map +1 -0
  338. package/dist/mail/drivers/memory-mail-driver.d.ts +14 -0
  339. package/dist/mail/drivers/memory-mail-driver.d.ts.map +1 -0
  340. package/dist/mail/drivers/memory-mail-driver.js +24 -0
  341. package/dist/mail/drivers/memory-mail-driver.js.map +1 -0
  342. package/dist/mail/index.d.ts +10 -0
  343. package/dist/mail/index.d.ts.map +1 -0
  344. package/dist/mail/index.js +10 -0
  345. package/dist/mail/index.js.map +1 -0
  346. package/dist/mail/mailer.d.ts +48 -0
  347. package/dist/mail/mailer.d.ts.map +1 -0
  348. package/dist/mail/mailer.js +111 -0
  349. package/dist/mail/mailer.js.map +1 -0
  350. package/dist/mail/message.d.ts +30 -0
  351. package/dist/mail/message.d.ts.map +1 -0
  352. package/dist/mail/message.js +77 -0
  353. package/dist/mail/message.js.map +1 -0
  354. package/dist/middleware/cors.d.ts +16 -0
  355. package/dist/middleware/cors.d.ts.map +1 -0
  356. package/dist/middleware/cors.js +76 -0
  357. package/dist/middleware/cors.js.map +1 -0
  358. package/dist/middleware.d.ts +26 -0
  359. package/dist/middleware.d.ts.map +1 -0
  360. package/dist/middleware.js +45 -0
  361. package/dist/middleware.js.map +1 -0
  362. package/dist/pdf/index.d.ts +2 -0
  363. package/dist/pdf/index.d.ts.map +1 -0
  364. package/dist/pdf/index.js +2 -0
  365. package/dist/pdf/index.js.map +1 -0
  366. package/dist/pdf/pdf.d.ts +86 -0
  367. package/dist/pdf/pdf.d.ts.map +1 -0
  368. package/dist/pdf/pdf.js +237 -0
  369. package/dist/pdf/pdf.js.map +1 -0
  370. package/dist/plugins/plugin.d.ts +25 -0
  371. package/dist/plugins/plugin.d.ts.map +1 -0
  372. package/dist/plugins/plugin.js +17 -0
  373. package/dist/plugins/plugin.js.map +1 -0
  374. package/dist/queue/drivers/database-queue-driver.d.ts +21 -0
  375. package/dist/queue/drivers/database-queue-driver.d.ts.map +1 -0
  376. package/dist/queue/drivers/database-queue-driver.js +84 -0
  377. package/dist/queue/drivers/database-queue-driver.js.map +1 -0
  378. package/dist/queue/drivers/memory-queue-driver.d.ts +17 -0
  379. package/dist/queue/drivers/memory-queue-driver.d.ts.map +1 -0
  380. package/dist/queue/drivers/memory-queue-driver.js +71 -0
  381. package/dist/queue/drivers/memory-queue-driver.js.map +1 -0
  382. package/dist/queue/drivers/queue-driver.d.ts +44 -0
  383. package/dist/queue/drivers/queue-driver.d.ts.map +1 -0
  384. package/dist/queue/drivers/queue-driver.js +6 -0
  385. package/dist/queue/drivers/queue-driver.js.map +1 -0
  386. package/dist/queue/drivers/redis-queue-driver.d.ts +16 -0
  387. package/dist/queue/drivers/redis-queue-driver.d.ts.map +1 -0
  388. package/dist/queue/drivers/redis-queue-driver.js +92 -0
  389. package/dist/queue/drivers/redis-queue-driver.js.map +1 -0
  390. package/dist/queue/index.d.ts +12 -0
  391. package/dist/queue/index.d.ts.map +1 -0
  392. package/dist/queue/index.js +12 -0
  393. package/dist/queue/index.js.map +1 -0
  394. package/dist/queue/job.d.ts +20 -0
  395. package/dist/queue/job.d.ts.map +1 -0
  396. package/dist/queue/job.js +19 -0
  397. package/dist/queue/job.js.map +1 -0
  398. package/dist/queue/queue-manager.d.ts +41 -0
  399. package/dist/queue/queue-manager.d.ts.map +1 -0
  400. package/dist/queue/queue-manager.js +98 -0
  401. package/dist/queue/queue-manager.js.map +1 -0
  402. package/dist/queue/worker.d.ts +57 -0
  403. package/dist/queue/worker.d.ts.map +1 -0
  404. package/dist/queue/worker.js +156 -0
  405. package/dist/queue/worker.js.map +1 -0
  406. package/dist/redis/index.d.ts +5 -0
  407. package/dist/redis/index.d.ts.map +1 -0
  408. package/dist/redis/index.js +5 -0
  409. package/dist/redis/index.js.map +1 -0
  410. package/dist/redis/redis-client.d.ts +44 -0
  411. package/dist/redis/redis-client.d.ts.map +1 -0
  412. package/dist/redis/redis-client.js +131 -0
  413. package/dist/redis/redis-client.js.map +1 -0
  414. package/dist/redis/redis-connection.d.ts +32 -0
  415. package/dist/redis/redis-connection.d.ts.map +1 -0
  416. package/dist/redis/redis-connection.js +151 -0
  417. package/dist/redis/redis-connection.js.map +1 -0
  418. package/dist/redis/redis-manager.d.ts +28 -0
  419. package/dist/redis/redis-manager.d.ts.map +1 -0
  420. package/dist/redis/redis-manager.js +68 -0
  421. package/dist/redis/redis-manager.js.map +1 -0
  422. package/dist/redis/resp-parser.d.ts +29 -0
  423. package/dist/redis/resp-parser.d.ts.map +1 -0
  424. package/dist/redis/resp-parser.js +113 -0
  425. package/dist/redis/resp-parser.js.map +1 -0
  426. package/dist/request.d.ts +122 -0
  427. package/dist/request.d.ts.map +1 -0
  428. package/dist/request.js +171 -0
  429. package/dist/request.js.map +1 -0
  430. package/dist/response.d.ts +208 -0
  431. package/dist/response.d.ts.map +1 -0
  432. package/dist/response.js +375 -0
  433. package/dist/response.js.map +1 -0
  434. package/dist/router.d.ts +146 -0
  435. package/dist/router.d.ts.map +1 -0
  436. package/dist/router.js +341 -0
  437. package/dist/router.js.map +1 -0
  438. package/dist/routing/controller.d.ts +18 -0
  439. package/dist/routing/controller.d.ts.map +1 -0
  440. package/dist/routing/controller.js +49 -0
  441. package/dist/routing/controller.js.map +1 -0
  442. package/dist/routing/named-middleware.d.ts +27 -0
  443. package/dist/routing/named-middleware.d.ts.map +1 -0
  444. package/dist/routing/named-middleware.js +41 -0
  445. package/dist/routing/named-middleware.js.map +1 -0
  446. package/dist/routing/route-builder.d.ts +54 -0
  447. package/dist/routing/route-builder.d.ts.map +1 -0
  448. package/dist/routing/route-builder.js +95 -0
  449. package/dist/routing/route-builder.js.map +1 -0
  450. package/dist/routing/route-group.d.ts +39 -0
  451. package/dist/routing/route-group.d.ts.map +1 -0
  452. package/dist/routing/route-group.js +79 -0
  453. package/dist/routing/route-group.js.map +1 -0
  454. package/dist/scheduler/scheduler.d.ts +85 -0
  455. package/dist/scheduler/scheduler.d.ts.map +1 -0
  456. package/dist/scheduler/scheduler.js +216 -0
  457. package/dist/scheduler/scheduler.js.map +1 -0
  458. package/dist/security/csrf.d.ts +19 -0
  459. package/dist/security/csrf.d.ts.map +1 -0
  460. package/dist/security/csrf.js +69 -0
  461. package/dist/security/csrf.js.map +1 -0
  462. package/dist/security/hash.d.ts +30 -0
  463. package/dist/security/hash.d.ts.map +1 -0
  464. package/dist/security/hash.js +86 -0
  465. package/dist/security/hash.js.map +1 -0
  466. package/dist/security/headers.d.ts +56 -0
  467. package/dist/security/headers.d.ts.map +1 -0
  468. package/dist/security/headers.js +81 -0
  469. package/dist/security/headers.js.map +1 -0
  470. package/dist/security/index.d.ts +13 -0
  471. package/dist/security/index.d.ts.map +1 -0
  472. package/dist/security/index.js +13 -0
  473. package/dist/security/index.js.map +1 -0
  474. package/dist/security/jwt.d.ts +96 -0
  475. package/dist/security/jwt.d.ts.map +1 -0
  476. package/dist/security/jwt.js +308 -0
  477. package/dist/security/jwt.js.map +1 -0
  478. package/dist/security/rate-limiter.d.ts +80 -0
  479. package/dist/security/rate-limiter.d.ts.map +1 -0
  480. package/dist/security/rate-limiter.js +127 -0
  481. package/dist/security/rate-limiter.js.map +1 -0
  482. package/dist/security/totp.d.ts +43 -0
  483. package/dist/security/totp.d.ts.map +1 -0
  484. package/dist/security/totp.js +122 -0
  485. package/dist/security/totp.js.map +1 -0
  486. package/dist/security/vault.d.ts +28 -0
  487. package/dist/security/vault.d.ts.map +1 -0
  488. package/dist/security/vault.js +73 -0
  489. package/dist/security/vault.js.map +1 -0
  490. package/dist/session/session-driver.d.ts +53 -0
  491. package/dist/session/session-driver.d.ts.map +1 -0
  492. package/dist/session/session-driver.js +132 -0
  493. package/dist/session/session-driver.js.map +1 -0
  494. package/dist/session/session-manager.d.ts +100 -0
  495. package/dist/session/session-manager.d.ts.map +1 -0
  496. package/dist/session/session-manager.js +186 -0
  497. package/dist/session/session-manager.js.map +1 -0
  498. package/dist/sse/index.d.ts +2 -0
  499. package/dist/sse/index.d.ts.map +1 -0
  500. package/dist/sse/index.js +2 -0
  501. package/dist/sse/index.js.map +1 -0
  502. package/dist/sse/sse.d.ts +32 -0
  503. package/dist/sse/sse.d.ts.map +1 -0
  504. package/dist/sse/sse.js +87 -0
  505. package/dist/sse/sse.js.map +1 -0
  506. package/dist/ssr/engine.d.ts +58 -0
  507. package/dist/ssr/engine.d.ts.map +1 -0
  508. package/dist/ssr/engine.js +121 -0
  509. package/dist/ssr/engine.js.map +1 -0
  510. package/dist/ssr/index.d.ts +6 -0
  511. package/dist/ssr/index.d.ts.map +1 -0
  512. package/dist/ssr/index.js +6 -0
  513. package/dist/ssr/index.js.map +1 -0
  514. package/dist/static/static.d.ts +22 -0
  515. package/dist/static/static.d.ts.map +1 -0
  516. package/dist/static/static.js +134 -0
  517. package/dist/static/static.js.map +1 -0
  518. package/dist/storage/chunked-uploader.d.ts +53 -0
  519. package/dist/storage/chunked-uploader.d.ts.map +1 -0
  520. package/dist/storage/chunked-uploader.js +135 -0
  521. package/dist/storage/chunked-uploader.js.map +1 -0
  522. package/dist/storage/drivers/local-driver.d.ts +25 -0
  523. package/dist/storage/drivers/local-driver.d.ts.map +1 -0
  524. package/dist/storage/drivers/local-driver.js +75 -0
  525. package/dist/storage/drivers/local-driver.js.map +1 -0
  526. package/dist/storage/drivers/memory-driver.d.ts +23 -0
  527. package/dist/storage/drivers/memory-driver.d.ts.map +1 -0
  528. package/dist/storage/drivers/memory-driver.js +55 -0
  529. package/dist/storage/drivers/memory-driver.js.map +1 -0
  530. package/dist/storage/drivers/s3-driver.d.ts +34 -0
  531. package/dist/storage/drivers/s3-driver.d.ts.map +1 -0
  532. package/dist/storage/drivers/s3-driver.js +186 -0
  533. package/dist/storage/drivers/s3-driver.js.map +1 -0
  534. package/dist/storage/drivers/storage-driver.d.ts +40 -0
  535. package/dist/storage/drivers/storage-driver.d.ts.map +1 -0
  536. package/dist/storage/drivers/storage-driver.js +6 -0
  537. package/dist/storage/drivers/storage-driver.js.map +1 -0
  538. package/dist/storage/index.d.ts +13 -0
  539. package/dist/storage/index.d.ts.map +1 -0
  540. package/dist/storage/index.js +13 -0
  541. package/dist/storage/index.js.map +1 -0
  542. package/dist/storage/multipart.d.ts +12 -0
  543. package/dist/storage/multipart.d.ts.map +1 -0
  544. package/dist/storage/multipart.js +79 -0
  545. package/dist/storage/multipart.js.map +1 -0
  546. package/dist/storage/storage-manager.d.ts +48 -0
  547. package/dist/storage/storage-manager.d.ts.map +1 -0
  548. package/dist/storage/storage-manager.js +106 -0
  549. package/dist/storage/storage-manager.js.map +1 -0
  550. package/dist/storage/uploaded-file.d.ts +40 -0
  551. package/dist/storage/uploaded-file.d.ts.map +1 -0
  552. package/dist/storage/uploaded-file.js +73 -0
  553. package/dist/storage/uploaded-file.js.map +1 -0
  554. package/dist/swagger/generator.d.ts +44 -0
  555. package/dist/swagger/generator.d.ts.map +1 -0
  556. package/dist/swagger/generator.js +117 -0
  557. package/dist/swagger/generator.js.map +1 -0
  558. package/dist/swagger/index.d.ts +8 -0
  559. package/dist/swagger/index.d.ts.map +1 -0
  560. package/dist/swagger/index.js +8 -0
  561. package/dist/swagger/index.js.map +1 -0
  562. package/dist/swagger/swagger.d.ts +9 -0
  563. package/dist/swagger/swagger.d.ts.map +1 -0
  564. package/dist/swagger/swagger.js +32 -0
  565. package/dist/swagger/swagger.js.map +1 -0
  566. package/dist/swagger/ui.d.ts +11 -0
  567. package/dist/swagger/ui.d.ts.map +1 -0
  568. package/dist/swagger/ui.js +93 -0
  569. package/dist/swagger/ui.js.map +1 -0
  570. package/dist/tcp/hl7.d.ts +19 -0
  571. package/dist/tcp/hl7.d.ts.map +1 -0
  572. package/dist/tcp/hl7.js +42 -0
  573. package/dist/tcp/hl7.js.map +1 -0
  574. package/dist/tcp/index.d.ts +4 -0
  575. package/dist/tcp/index.d.ts.map +1 -0
  576. package/dist/tcp/index.js +4 -0
  577. package/dist/tcp/index.js.map +1 -0
  578. package/dist/tcp/mllp.d.ts +22 -0
  579. package/dist/tcp/mllp.d.ts.map +1 -0
  580. package/dist/tcp/mllp.js +44 -0
  581. package/dist/tcp/mllp.js.map +1 -0
  582. package/dist/tcp/tcp-server.d.ts +20 -0
  583. package/dist/tcp/tcp-server.d.ts.map +1 -0
  584. package/dist/tcp/tcp-server.js +59 -0
  585. package/dist/tcp/tcp-server.js.map +1 -0
  586. package/dist/tenancy/index.d.ts +2 -0
  587. package/dist/tenancy/index.d.ts.map +1 -0
  588. package/dist/tenancy/index.js +2 -0
  589. package/dist/tenancy/index.js.map +1 -0
  590. package/dist/tenancy/tenant.d.ts +22 -0
  591. package/dist/tenancy/tenant.d.ts.map +1 -0
  592. package/dist/tenancy/tenant.js +32 -0
  593. package/dist/tenancy/tenant.js.map +1 -0
  594. package/dist/testing/test-client.d.ts +36 -0
  595. package/dist/testing/test-client.d.ts.map +1 -0
  596. package/dist/testing/test-client.js +128 -0
  597. package/dist/testing/test-client.js.map +1 -0
  598. package/dist/validation/index.d.ts +10 -0
  599. package/dist/validation/index.d.ts.map +1 -0
  600. package/dist/validation/index.js +10 -0
  601. package/dist/validation/index.js.map +1 -0
  602. package/dist/validation/rules-validator.d.ts +65 -0
  603. package/dist/validation/rules-validator.d.ts.map +1 -0
  604. package/dist/validation/rules-validator.js +301 -0
  605. package/dist/validation/rules-validator.js.map +1 -0
  606. package/dist/validation/schema.d.ts +34 -0
  607. package/dist/validation/schema.d.ts.map +1 -0
  608. package/dist/validation/schema.js +174 -0
  609. package/dist/validation/schema.js.map +1 -0
  610. package/dist/validation/vine.d.ts +23 -0
  611. package/dist/validation/vine.d.ts.map +1 -0
  612. package/dist/validation/vine.js +97 -0
  613. package/dist/validation/vine.js.map +1 -0
  614. package/dist/views/view.d.ts +51 -0
  615. package/dist/views/view.d.ts.map +1 -0
  616. package/dist/views/view.js +91 -0
  617. package/dist/views/view.js.map +1 -0
  618. package/dist/views/vite.d.ts +19 -0
  619. package/dist/views/vite.d.ts.map +1 -0
  620. package/dist/views/vite.js +60 -0
  621. package/dist/views/vite.js.map +1 -0
  622. package/dist/webhook/index.d.ts +2 -0
  623. package/dist/webhook/index.d.ts.map +1 -0
  624. package/dist/webhook/index.js +2 -0
  625. package/dist/webhook/index.js.map +1 -0
  626. package/dist/webhook/webhook.d.ts +30 -0
  627. package/dist/webhook/webhook.d.ts.map +1 -0
  628. package/dist/webhook/webhook.js +124 -0
  629. package/dist/webhook/webhook.js.map +1 -0
  630. package/dist/ws/broadcaster.d.ts +43 -0
  631. package/dist/ws/broadcaster.d.ts.map +1 -0
  632. package/dist/ws/broadcaster.js +81 -0
  633. package/dist/ws/broadcaster.js.map +1 -0
  634. package/dist/ws/channel.d.ts +23 -0
  635. package/dist/ws/channel.d.ts.map +1 -0
  636. package/dist/ws/channel.js +27 -0
  637. package/dist/ws/channel.js.map +1 -0
  638. package/dist/ws/index.d.ts +5 -0
  639. package/dist/ws/index.d.ts.map +1 -0
  640. package/dist/ws/index.js +5 -0
  641. package/dist/ws/index.js.map +1 -0
  642. package/dist/ws/websocket-hub.d.ts +52 -0
  643. package/dist/ws/websocket-hub.d.ts.map +1 -0
  644. package/dist/ws/websocket-hub.js +187 -0
  645. package/dist/ws/websocket-hub.js.map +1 -0
  646. package/dist/ws/websocket.d.ts +43 -0
  647. package/dist/ws/websocket.d.ts.map +1 -0
  648. package/dist/ws/websocket.js +214 -0
  649. package/dist/ws/websocket.js.map +1 -0
  650. package/package.json +141 -0
@@ -0,0 +1,1760 @@
1
+ /**
2
+ * @file ecommerce-erp.ts
3
+ * @description Official Omnichannel E-Commerce & Nursery ERP full-stack starter template generator.
4
+ */
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ function writeFileWithDir(filePath, content) {
8
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
9
+ fs.writeFileSync(filePath, content.trimStart(), 'utf-8');
10
+ }
11
+ export function scaffoldEcommerceErp(dir, projectName) {
12
+ const createdFiles = [];
13
+ const addFile = (relPath, content) => {
14
+ const fullPath = path.join(dir, relPath);
15
+ writeFileWithDir(fullPath, content);
16
+ createdFiles.push(fullPath);
17
+ };
18
+ // 1. package.json
19
+ addFile('package.json', `{
20
+ "name": "${projectName}",
21
+ "version": "1.0.0",
22
+ "description": "Omnichannel E-Commerce & Nursery ERP Backend powered by AeroJS",
23
+ "type": "module",
24
+ "main": "server.ts",
25
+ "scripts": {
26
+ "dev": "concurrently \\"tsx watch server.ts\\" \\"vite\\"",
27
+ "server": "tsx watch server.ts",
28
+ "build": "vite build && tsc",
29
+ "start": "node dist/server.js",
30
+ "cli": "tsx cli.ts",
31
+ "test": "vitest run",
32
+ "db:seed": "tsx database/seed.ts"
33
+ },
34
+ "dependencies": {
35
+ "@inertiajs/vue3": "^1.2.0",
36
+ "aerojs": "file:../AeroJS",
37
+ "knex": "^3.3.0",
38
+ "mysql2": "^3.11.0",
39
+ "vue": "^3.4.38"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.0.0",
43
+ "@vitejs/plugin-vue": "^5.1.2",
44
+ "concurrently": "^8.2.2",
45
+ "tsx": "^4.19.0",
46
+ "typescript": "^5.6.0",
47
+ "vite": "^5.4.2",
48
+ "vitest": "^2.0.0"
49
+ }
50
+ }
51
+ `);
52
+ // 2. tsconfig.json
53
+ addFile('tsconfig.json', `{
54
+ "compilerOptions": {
55
+ "target": "ES2022",
56
+ "module": "NodeNext",
57
+ "moduleResolution": "NodeNext",
58
+ "esModuleInterop": true,
59
+ "experimentalDecorators": true,
60
+ "emitDecoratorMetadata": true,
61
+ "strict": true,
62
+ "skipLibCheck": true,
63
+ "jsx": "preserve",
64
+ "outDir": "./dist"
65
+ },
66
+ "include": [
67
+ "app/**/*",
68
+ "config/**/*",
69
+ "routes/**/*",
70
+ "database/**/*",
71
+ "resources/**/*",
72
+ "server.ts",
73
+ "cli.ts",
74
+ "tests/**/*"
75
+ ]
76
+ }
77
+ `);
78
+ // 3. vite.config.ts
79
+ addFile('vite.config.ts', `import { defineConfig } from 'vite';
80
+ import vue from '@vitejs/plugin-vue';
81
+ import path from 'path';
82
+
83
+ export default defineConfig({
84
+ plugins: [vue()],
85
+ publicDir: false,
86
+ resolve: {
87
+ alias: {
88
+ '@': path.resolve(__dirname, 'resources/js'),
89
+ },
90
+ },
91
+ server: {
92
+ cors: true,
93
+ port: 5173,
94
+ strictPort: true,
95
+ },
96
+ build: {
97
+ outDir: 'public/build',
98
+ manifest: true,
99
+ rollupOptions: {
100
+ input: 'resources/js/app.ts',
101
+ },
102
+ },
103
+ });
104
+ `);
105
+ // 4. .env & .env.example
106
+ const envContent = `PORT=3000
107
+ NODE_ENV=development
108
+ APP_NAME=NurseryERP
109
+ APP_KEY=nursery-erp-secret-key-32chars-min-length-123456
110
+
111
+ # Database
112
+ DB_CONNECTION=mysql
113
+ DB_HOST=127.0.0.1
114
+ DB_PORT=3306
115
+ DB_USER=root
116
+ DB_PASSWORD=
117
+ DB_DATABASE=nursery_erp_db
118
+ DB_POOL_MIN=2
119
+ DB_POOL_MAX=20
120
+
121
+ # JWT Auth
122
+ JWT_SECRET=nursery-erp-jwt-secret-key-for-admin-and-staff
123
+
124
+ # 3PL Courier Credentials
125
+ STEADFAST_API_KEY=
126
+ STEADFAST_SECRET_KEY=
127
+ PATHAO_CLIENT_ID=
128
+ PATHAO_CLIENT_SECRET=
129
+ PATHAO_STORE_ID=
130
+ `;
131
+ addFile('.env', envContent);
132
+ addFile('.env.example', envContent);
133
+ // 5. Config files
134
+ addFile('config/app.ts', `export const appConfig = {
135
+ name: process.env.APP_NAME || 'NurseryERP',
136
+ port: parseInt(process.env.PORT || '3000', 10),
137
+ env: process.env.NODE_ENV || 'development',
138
+ debug: process.env.NODE_ENV !== 'production',
139
+ key: process.env.APP_KEY || 'default-secret-key-32chars-min-length-123',
140
+ jwtSecret: process.env.JWT_SECRET || 'nursery-erp-jwt-secret-key-for-admin-and-staff',
141
+ };
142
+ `);
143
+ addFile('config/database.ts', `export const databaseConfig = {
144
+ default: (process.env.DB_CONNECTION || 'mysql').toLowerCase(),
145
+ connections: {
146
+ mysql: {
147
+ client: 'mysql2',
148
+ host: process.env.DB_HOST || '127.0.0.1',
149
+ port: parseInt(process.env.DB_PORT || '3306', 10),
150
+ user: process.env.DB_USER || 'root',
151
+ password: process.env.DB_PASSWORD || '',
152
+ database: process.env.DB_DATABASE || 'nursery_erp_db',
153
+ },
154
+ memory: {
155
+ client: 'memory',
156
+ },
157
+ },
158
+ pool: {
159
+ min: parseInt(process.env.DB_POOL_MIN || '2', 10),
160
+ max: parseInt(process.env.DB_POOL_MAX || '20', 10),
161
+ },
162
+ };
163
+ `);
164
+ addFile('config/security.ts', `export const securityConfig = {
165
+ jwt: {
166
+ secret: process.env.JWT_SECRET || 'nursery-erp-jwt-secret-key-for-admin-and-staff',
167
+ expiresIn: '7d',
168
+ },
169
+ };
170
+ `);
171
+ // 6. Database Connection & Seed
172
+ addFile('database/connection.ts', `import knex, { type Knex } from 'knex';
173
+ import { loadEnv, useKnex, DB, Database } from 'aerojs';
174
+ import { databaseConfig } from '../config/database.js';
175
+
176
+ loadEnv();
177
+
178
+ let knexInstance: Knex | null = null;
179
+
180
+ export function getKnex(): Knex {
181
+ if (!knexInstance) {
182
+ knexInstance = knex({
183
+ client: 'mysql2',
184
+ connection: {
185
+ host: databaseConfig.connections.mysql.host,
186
+ port: databaseConfig.connections.mysql.port,
187
+ user: databaseConfig.connections.mysql.user,
188
+ password: databaseConfig.connections.mysql.password,
189
+ database: databaseConfig.connections.mysql.database,
190
+ },
191
+ pool: {
192
+ min: databaseConfig.pool.min,
193
+ max: databaseConfig.pool.max,
194
+ },
195
+ });
196
+
197
+ useKnex(knexInstance);
198
+ }
199
+ return knexInstance;
200
+ }
201
+
202
+ export async function initDatabase(): Promise<Knex> {
203
+ const instance = getKnex();
204
+ await instance.raw('SELECT 1 + 1 AS result');
205
+ return instance;
206
+ }
207
+
208
+ export async function closeDatabase(): Promise<void> {
209
+ if (knexInstance) {
210
+ await knexInstance.destroy();
211
+ knexInstance = null;
212
+ }
213
+ }
214
+
215
+ export { DB, Database };
216
+ `);
217
+ addFile('database/seed.ts', `import { initDatabase, DB, closeDatabase } from './connection.js';
218
+
219
+ export async function seed() {
220
+ await initDatabase();
221
+ console.log('Seeding default ERP Chart of Accounts...');
222
+
223
+ const existingGroups = await DB.table('tbl_ledger_group').count();
224
+ if (Number(existingGroups) === 0) {
225
+ await DB.table('tbl_ledger_group').insert([
226
+ { h_name: 'Current Assets', code: '1000', type: 'Asset', ed: 1 },
227
+ { h_name: 'Current Liabilities', code: '2000', type: 'Liability', ed: 1 },
228
+ { h_name: 'Operating Income', code: '3000', type: 'Income', ed: 1 },
229
+ { h_name: 'Operating Expense', code: '4000', type: 'Expense', ed: 1 },
230
+ { h_name: 'Owner Equity', code: '5000', type: 'Equity', ed: 1 },
231
+ ]);
232
+ console.log('Inserted default ledger groups.');
233
+ }
234
+
235
+ const existingLedgers = await DB.table('tbl_ledger').count();
236
+ if (Number(existingLedgers) === 0) {
237
+ await DB.table('tbl_ledger').insert([
238
+ { h_id: 1, l_name: 'Cash in Hand', l_balance: 0, c_balance: 0, ed: 1 },
239
+ { h_id: 1, l_name: 'Bank Account (BRAC)', l_balance: 0, c_balance: 0, ed: 1 },
240
+ { h_id: 1, l_name: 'Inventory Asset Account', l_balance: 0, c_balance: 0, ed: 1 },
241
+ { h_id: 1, l_name: 'Courier Clearing Account', l_balance: 0, c_balance: 0, ed: 1 },
242
+ { h_id: 3, l_name: 'Sales Revenue', l_balance: 0, c_balance: 0, ed: 1 },
243
+ { h_id: 4, l_name: 'Cost of Goods Sold (COGS)', l_balance: 0, c_balance: 0, ed: 1 },
244
+ { h_id: 4, l_name: 'Delivery & Shipping Expense', l_balance: 0, c_balance: 0, ed: 1 },
245
+ ]);
246
+ console.log('Inserted default ledgers.');
247
+ }
248
+
249
+ console.log('Seeding completed successfully!');
250
+ await closeDatabase();
251
+ }
252
+
253
+ if (process.argv[1]?.includes('seed.ts')) {
254
+ seed().catch(console.error);
255
+ }
256
+ `);
257
+ // 7. Active Record Models
258
+ addFile('app/models/Product.ts', `import { Model } from 'aerojs';
259
+ import { ProductLandingPage } from './ProductLandingPage.js';
260
+ import { OnlineOrderItem } from './OnlineOrderItem.js';
261
+ import { ProductTransaction } from './ProductTransaction.js';
262
+ import { SaleDetail } from './SaleDetail.js';
263
+
264
+ export class Product extends Model {
265
+ public static override table = 'tbl_product';
266
+ public static override primaryKey = 'product_id';
267
+ public static override timestamps = false;
268
+
269
+ public static override fillable = [
270
+ 'parent_id', 'sg_id', 'web_category_id', 'sc_id', 'su_id', 'part_no',
271
+ 'product_name', 'slug', 'image', 'description', 'scientific_name',
272
+ 'plant_type', 'sunlight', 'watering', 'fruiting_time', 'pot_size',
273
+ 'care_tips', 'is_featured', 'is_online_visible', 'is_plant', 'base_uom',
274
+ 'sub_uom', 'conversion_factor', 'pak_size', 'in_rate', 'bata_rate',
275
+ 'per_rate', 'cost_rate', 'cgp_rate', 'sale_rate', 'tp_rate', 'dp_rate',
276
+ 'mrp_rate', 'w_id', 'o_stock', 'o_rate', 'o_amount', 'o_date',
277
+ 'c_stock', 'avg_rate', 'entry_time', 'product_type', 'status'
278
+ ];
279
+
280
+ public landingPage() {
281
+ return this.hasOne(ProductLandingPage, 'product_id', 'product_id');
282
+ }
283
+
284
+ public orderItems() {
285
+ return this.hasMany(OnlineOrderItem, 'product_id', 'product_id');
286
+ }
287
+
288
+ public transactions() {
289
+ return this.hasMany(ProductTransaction, 'pb_id', 'product_id');
290
+ }
291
+
292
+ public saleDetails() {
293
+ return this.hasMany(SaleDetail, 'product_id', 'product_id');
294
+ }
295
+ }
296
+ `);
297
+ addFile('app/models/ProductLandingPage.ts', `import { Model } from 'aerojs';
298
+ import { Product } from './Product.js';
299
+
300
+ export class ProductLandingPage extends Model {
301
+ public static override table = 'tbl_product_landing_pages';
302
+ public static override primaryKey = 'id';
303
+ public static override timestamps = true;
304
+
305
+ public static override fillable = [
306
+ 'product_id', 'slug', 'headline', 'subheadline', 'badge_text',
307
+ 'video_url', 'banner_image', 'gallery_images_json', 'features_json',
308
+ 'packages_json', 'guarantee_text', 'reviews_json', 'faq_json',
309
+ 'fb_pixel_id', 'seo_meta_title', 'seo_meta_description',
310
+ 'seo_meta_keywords', 'og_image', 'custom_head_scripts', 'views_count',
311
+ 'orders_count', 'is_active'
312
+ ];
313
+
314
+ public product() {
315
+ return this.belongsTo(Product, 'product_id', 'product_id');
316
+ }
317
+ }
318
+ `);
319
+ addFile('app/models/OnlineOrderItem.ts', `import { Model } from 'aerojs';
320
+ import { OnlineOrder } from './OnlineOrder.js';
321
+ import { Product } from './Product.js';
322
+
323
+ export class OnlineOrderItem extends Model {
324
+ public static override table = 'tbl_online_order_items';
325
+ public static override primaryKey = 'id';
326
+ public static override timestamps = false;
327
+
328
+ public static override fillable = [
329
+ 'order_id', 'product_id', 'product_name', 'unit_price', 'qty',
330
+ 'subtotal', 'image_url', 'created_at'
331
+ ];
332
+
333
+ public order() {
334
+ return this.belongsTo(OnlineOrder, 'order_id', 'id');
335
+ }
336
+
337
+ public product() {
338
+ return this.belongsTo(Product, 'product_id', 'product_id');
339
+ }
340
+ }
341
+ `);
342
+ addFile('app/models/OnlineOrder.ts', `import { Model } from 'aerojs';
343
+ import { OnlineOrderItem } from './OnlineOrderItem.js';
344
+ import { SaleHead } from './SaleHead.js';
345
+
346
+ export class OnlineOrder extends Model {
347
+ public static override table = 'tbl_online_orders';
348
+ public static override primaryKey = 'id';
349
+ public static override timestamps = true;
350
+
351
+ public static override fillable = [
352
+ 'order_no', 'customer_name', 'customer_phone', 'customer_address',
353
+ 'delivery_district', 'total_items_count', 'total_amount', 'payment_method',
354
+ 'courier_name', 'consignment_no', 'courier_tracking_id', 'courier_status',
355
+ 'courier_delivery_fee', 'pathao_city_id', 'pathao_zone_id', 'pathao_area_id',
356
+ 'order_notes', 'delivery_charge', 'status', 'sale_id'
357
+ ];
358
+
359
+ public items() {
360
+ return this.hasMany(OnlineOrderItem, 'order_id', 'id');
361
+ }
362
+
363
+ public saleHead() {
364
+ return this.belongsTo(SaleHead, 'sale_id', 'id');
365
+ }
366
+ }
367
+ `);
368
+ addFile('app/models/SaleHead.ts', `import { Model } from 'aerojs';
369
+ import { SaleDetail } from './SaleDetail.js';
370
+ import { Ledger } from './Ledger.js';
371
+ import { OnlineOrder } from './OnlineOrder.js';
372
+ import { DeliveryChallan } from './DeliveryChallan.js';
373
+
374
+ export class SaleHead extends Model {
375
+ public static override table = 'tbl_sale_head';
376
+ public static override primaryKey = 'id';
377
+ public static override timestamps = false;
378
+
379
+ public static override fillable = [
380
+ 'vh_no', 'bill_no', 'vh_date', 'l_id', 's_id', 'c_id', 'w_id',
381
+ 'qty', 'total_amount', 'other_cost', 'discount', 'cash_amount',
382
+ 'net_amount', 'prev_balance', 'narration', 'sale_type', 'entry_time'
383
+ ];
384
+
385
+ public details() {
386
+ return this.hasMany(SaleDetail, 'vh_id', 'id');
387
+ }
388
+
389
+ public ledger() {
390
+ return this.belongsTo(Ledger, 'l_id', 'l_id');
391
+ }
392
+
393
+ public order() {
394
+ return this.hasOne(OnlineOrder, 'sale_id', 'id');
395
+ }
396
+
397
+ public challan() {
398
+ return this.hasOne(DeliveryChallan, 'sale_id', 'id');
399
+ }
400
+ }
401
+ `);
402
+ addFile('app/models/SaleDetail.ts', `import { Model } from 'aerojs';
403
+ import { SaleHead } from './SaleHead.js';
404
+ import { Product } from './Product.js';
405
+
406
+ export class SaleDetail extends Model {
407
+ public static override table = 'tbl_sale_detail';
408
+ public static override primaryKey = 'id';
409
+ public static override timestamps = false;
410
+
411
+ public static override fillable = [
412
+ 'vh_id', 'vh_date', 'product_id', 'w_id', 'in_rate', 'bata_rate',
413
+ 'qty', 'cost_rate', 'cgp_rate', 'amount', 'entry_time', 'expiry_date'
414
+ ];
415
+
416
+ public saleHead() {
417
+ return this.belongsTo(SaleHead, 'vh_id', 'id');
418
+ }
419
+
420
+ public product() {
421
+ return this.belongsTo(Product, 'product_id', 'product_id');
422
+ }
423
+ }
424
+ `);
425
+ addFile('app/models/DeliveryChallan.ts', `import { Model } from 'aerojs';
426
+ import { SaleHead } from './SaleHead.js';
427
+
428
+ export class DeliveryChallan extends Model {
429
+ public static override table = 'tbl_delivery_challans';
430
+ public static override primaryKey = 'id';
431
+ public static override timestamps = false;
432
+
433
+ public static override fillable = [
434
+ 'challan_no', 'gate_pass_no', 'sale_id', 'customer_id', 'customer_name',
435
+ 'customer_phone', 'delivery_address', 'vehicle_type', 'vehicle_no',
436
+ 'driver_name', 'driver_phone', 'truck_fare', 'fare_paid_by',
437
+ 'total_plants_qty', 'delivery_date', 'delivery_time', 'notes',
438
+ 'status', 'created_by', 'created_at'
439
+ ];
440
+
441
+ public saleHead() {
442
+ return this.belongsTo(SaleHead, 'sale_id', 'id');
443
+ }
444
+ }
445
+ `);
446
+ addFile('app/models/ProductTransaction.ts', `import { Model } from 'aerojs';
447
+ import { Product } from './Product.js';
448
+
449
+ export class ProductTransaction extends Model {
450
+ public static override table = 'tbl_product_transactions';
451
+ public static override primaryKey = 'id';
452
+ public static override timestamps = false;
453
+
454
+ public static override fillable = [
455
+ 'pb_id', 'w_id', 'vh_id', 'vh_no', 'vh_date', 'type', 'qty',
456
+ 'rate', 'value', 'entrytime', 'expiry_date'
457
+ ];
458
+
459
+ public product() {
460
+ return this.belongsTo(Product, 'pb_id', 'product_id');
461
+ }
462
+ }
463
+ `);
464
+ addFile('app/models/LedgerGroup.ts', `import { Model } from 'aerojs';
465
+ import { Ledger } from './Ledger.js';
466
+
467
+ export class LedgerGroup extends Model {
468
+ public static override table = 'tbl_ledger_group';
469
+ public static override primaryKey = 'h_id';
470
+ public static override timestamps = false;
471
+
472
+ public static override fillable = [
473
+ 'h_name', 'code', 'parent_id', 'type', 'ed', 'entry_time'
474
+ ];
475
+
476
+ public ledgers() {
477
+ return this.hasMany(Ledger, 'h_id', 'h_id');
478
+ }
479
+ }
480
+ `);
481
+ addFile('app/models/Ledger.ts', `import { Model } from 'aerojs';
482
+ import { LedgerGroup } from './LedgerGroup.js';
483
+ import { LedgerTransaction } from './LedgerTransaction.js';
484
+
485
+ export class Ledger extends Model {
486
+ public static override table = 'tbl_ledger';
487
+ public static override primaryKey = 'l_id';
488
+ public static override timestamps = false;
489
+
490
+ public static override fillable = [
491
+ 'h_id', 'l_name', 'l_address', 'l_narration', 'l_mobile', 'l_pro_name',
492
+ 'l_pro_mobile', 'l_balance', 'c_balance', 'credit_limit', 'foreign_currency',
493
+ 'country', 'route_id', 'salesman_id', 'open_date', 'ed', 'entry_time'
494
+ ];
495
+
496
+ public group() {
497
+ return this.belongsTo(LedgerGroup, 'h_id', 'h_id');
498
+ }
499
+
500
+ public transactions() {
501
+ return this.hasMany(LedgerTransaction, 'l_id', 'l_id');
502
+ }
503
+ }
504
+ `);
505
+ addFile('app/models/LedgerTransaction.ts', `import { Model } from 'aerojs';
506
+ import { Ledger } from './Ledger.js';
507
+
508
+ export class LedgerTransaction extends Model {
509
+ public static override table = 'tbl_ledger_transactions';
510
+ public static override primaryKey = 'id';
511
+ public static override timestamps = false;
512
+
513
+ public static override fillable = [
514
+ 'vh_id', 'vh_no', 'vh_date', 'l_id', 'for_l_id', 'type', 'notes',
515
+ 'dr_amt', 'cr_amt', 'amount', 'entrytime'
516
+ ];
517
+
518
+ public ledger() {
519
+ return this.belongsTo(Ledger, 'l_id', 'l_id');
520
+ }
521
+
522
+ public counterLedger() {
523
+ return this.belongsTo(Ledger, 'for_l_id', 'l_id');
524
+ }
525
+ }
526
+ `);
527
+ addFile('app/models/Employee.ts', `import { Model } from 'aerojs';
528
+ import { Ledger } from './Ledger.js';
529
+
530
+ export class Employee extends Model {
531
+ public static override table = 'tbl_employees';
532
+ public static override primaryKey = 'id';
533
+ public static override timestamps = false;
534
+
535
+ public static override fillable = [
536
+ 'l_id', 'emp_id', 'name', 'designation', 'department', 'salary_type',
537
+ 'basic_salary', 'daily_rate', 'morning_rate', 'afternoon_rate',
538
+ 'join_date', 'status', 'created_at'
539
+ ];
540
+
541
+ public ledger() {
542
+ return this.belongsTo(Ledger, 'l_id', 'l_id');
543
+ }
544
+ }
545
+ `);
546
+ addFile('app/models/index.ts', `export * from './Product.js';
547
+ export * from './ProductLandingPage.js';
548
+ export * from './OnlineOrder.js';
549
+ export * from './OnlineOrderItem.js';
550
+ export * from './ProductTransaction.js';
551
+ export * from './DeliveryChallan.js';
552
+ export * from './SaleHead.js';
553
+ export * from './SaleDetail.js';
554
+ export * from './LedgerGroup.js';
555
+ export * from './Ledger.js';
556
+ export * from './LedgerTransaction.js';
557
+ export * from './Employee.js';
558
+ `);
559
+ // 8. Services
560
+ addFile('app/services/ProductService.ts', `import { Product } from '../models/Product.js';
561
+ import { ProductLandingPage } from '../models/ProductLandingPage.js';
562
+
563
+ export class ProductService {
564
+ public async paginate(page = 1, limit = 20, search = '') {
565
+ let query = Product.query();
566
+ if (search) {
567
+ query = query.where('product_name', 'LIKE', \`%\${search}%\`);
568
+ }
569
+ return query.paginate(page, limit);
570
+ }
571
+
572
+ public async findById(id: number | string) {
573
+ return Product.find(id);
574
+ }
575
+
576
+ public async findBySlug(slug: string) {
577
+ const landing = await ProductLandingPage.where('slug', slug).first();
578
+ if (landing) {
579
+ const product = await Product.find(landing.get('product_id'));
580
+ return { landing, product };
581
+ }
582
+ const product = await Product.where('slug', slug).first();
583
+ return { product, landing: null };
584
+ }
585
+
586
+ public async checkAtpStock(productId: number | string): Promise<number> {
587
+ const product = await Product.find(productId);
588
+ if (!product) return 0;
589
+ return Number(product.get('c_stock') || 0);
590
+ }
591
+ }
592
+ `);
593
+ addFile('app/services/OrderService.ts', `import { DB } from '../../database/connection.js';
594
+ import { OnlineOrder } from '../models/OnlineOrder.js';
595
+ import { OnlineOrderItem } from '../models/OnlineOrderItem.js';
596
+ import { Product } from '../models/Product.js';
597
+ import { SaleHead } from '../models/SaleHead.js';
598
+ import { SaleDetail } from '../models/SaleDetail.js';
599
+ import { DeliveryChallan } from '../models/DeliveryChallan.js';
600
+ import { LedgerService } from './LedgerService.js';
601
+
602
+ export class OrderService {
603
+ private ledgerService = new LedgerService();
604
+
605
+ public async createOrder(data: {
606
+ customer_name: string;
607
+ customer_phone: string;
608
+ customer_address: string;
609
+ delivery_district: string;
610
+ payment_method?: string;
611
+ order_notes?: string;
612
+ delivery_charge?: number;
613
+ items: Array<{ product_id: number; qty: number; unit_price?: number }>;
614
+ }) {
615
+ return DB.transaction(async (trx) => {
616
+ const orderNo = 'ORD-' + Date.now();
617
+ let totalAmount = 0;
618
+ let totalItems = 0;
619
+ const orderItemsToInsert: any[] = [];
620
+
621
+ for (const item of data.items) {
622
+ const product = await Product.find(item.product_id, trx);
623
+ if (!product) throw new Error(\`Product ID \${item.product_id} not found\`);
624
+ const price = item.unit_price !== undefined ? item.unit_price : Number(product.get('sale_rate') || 0);
625
+ const subtotal = price * item.qty;
626
+ totalAmount += subtotal;
627
+ totalItems += item.qty;
628
+
629
+ orderItemsToInsert.push({
630
+ product_id: product.get('product_id'),
631
+ product_name: product.get('product_name'),
632
+ unit_price: price,
633
+ qty: item.qty,
634
+ subtotal,
635
+ image_url: product.get('image'),
636
+ created_at: new Date(),
637
+ });
638
+ }
639
+
640
+ const deliveryCharge = data.delivery_charge || 120;
641
+ const grandTotal = totalAmount + deliveryCharge;
642
+
643
+ const orderResult = await trx.execute(
644
+ \`INSERT INTO tbl_online_orders
645
+ (order_no, customer_name, customer_phone, customer_address, delivery_district, total_items_count, total_amount, payment_method, delivery_charge, status, created_at, updated_at)
646
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NOW(), NOW())\`,
647
+ [orderNo, data.customer_name, data.customer_phone, data.customer_address, data.delivery_district, totalItems, grandTotal, data.payment_method || 'cod', deliveryCharge]
648
+ );
649
+
650
+ const orderId = orderResult.insertId;
651
+
652
+ for (const oi of orderItemsToInsert) {
653
+ await trx.execute(
654
+ \`INSERT INTO tbl_online_order_items (order_id, product_id, product_name, unit_price, qty, subtotal, image_url, created_at)
655
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)\`,
656
+ [orderId, oi.product_id, oi.product_name, oi.unit_price, oi.qty, oi.subtotal, oi.image_url, oi.created_at]
657
+ );
658
+ }
659
+
660
+ return { id: orderId, order_no: orderNo, total_amount: grandTotal, status: 'pending' };
661
+ });
662
+ }
663
+
664
+ public async confirmOrder(orderId: number | string) {
665
+ return DB.transaction(async (trx) => {
666
+ const order = await OnlineOrder.findOrFail(orderId, trx);
667
+ if (order.get('status') === 'confirmed' || order.get('status') === 'delivered') {
668
+ return order;
669
+ }
670
+
671
+ const items = await OnlineOrderItem.where('order_id', orderId).get(trx);
672
+ const vhNo = 'INV-' + Date.now();
673
+ const vhDate = new Date().toISOString().slice(0, 10);
674
+ const netAmount = Number(order.get('total_amount') || 0);
675
+
676
+ // 1. Create Sale Voucher (tbl_sale_head)
677
+ const saleResult = await trx.execute(
678
+ \`INSERT INTO tbl_sale_head (vh_no, bill_no, vh_date, l_id, qty, total_amount, net_amount, narration, sale_type, entry_time)
679
+ VALUES (?, ?, ?, 1, ?, ?, ?, ?, 1, NOW())\`,
680
+ [vhNo, order.get('order_no'), vhDate, order.get('total_items_count'), netAmount, netAmount, \`Sale for Online Order \${order.get('order_no')}\`]
681
+ );
682
+ const saleId = saleResult.insertId;
683
+
684
+ // 2. Insert Sale Details & Deduct Stock
685
+ for (const item of items) {
686
+ await trx.execute(
687
+ \`INSERT INTO tbl_sale_detail (vh_id, vh_date, product_id, qty, amount, entry_time)
688
+ VALUES (?, ?, ?, ?, ?, NOW())\`,
689
+ [saleId, vhDate, item.get('product_id'), item.get('qty'), item.get('subtotal')]
690
+ );
691
+
692
+ // Deduct inventory
693
+ await trx.execute(
694
+ \`UPDATE tbl_product SET c_stock = c_stock - ? WHERE product_id = ?\`,
695
+ [item.get('qty'), item.get('product_id')]
696
+ );
697
+
698
+ // Record stock movement transaction
699
+ await trx.execute(
700
+ \`INSERT INTO tbl_product_transactions (pb_id, vh_id, vh_no, vh_date, type, qty, value, entrytime)
701
+ VALUES (?, ?, ?, ?, 'sale', ?, ?, NOW())\`,
702
+ [item.get('product_id'), saleId, vhNo, vhDate, item.get('qty'), item.get('subtotal')]
703
+ );
704
+ }
705
+
706
+ // 3. Create Delivery Challan
707
+ const challanNo = 'CHL-' + Date.now();
708
+ await trx.execute(
709
+ \`INSERT INTO tbl_delivery_challans (challan_no, sale_id, customer_name, customer_phone, delivery_address, total_plants_qty, delivery_date, status, created_at)
710
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'dispatched', NOW())\`,
711
+ [challanNo, saleId, order.get('customer_name'), order.get('customer_phone'), order.get('customer_address'), order.get('total_items_count'), vhDate]
712
+ );
713
+
714
+ // 4. Update Online Order
715
+ await trx.execute(
716
+ \`UPDATE tbl_online_orders SET status = 'confirmed', sale_id = ?, updated_at = NOW() WHERE id = ?\`,
717
+ [saleId, orderId]
718
+ );
719
+
720
+ // 5. Post 4-Leg Double-Entry GL Journal
721
+ await this.ledgerService.postDeliveryVoucher(saleId, netAmount, trx);
722
+
723
+ return { success: true, order_id: orderId, sale_id: saleId, vh_no: vhNo, challan_no: challanNo };
724
+ });
725
+ }
726
+
727
+ public async paginate(page = 1, limit = 20, status?: string) {
728
+ let query = OnlineOrder.query().orderBy('id', 'DESC');
729
+ if (status) {
730
+ query = query.where('status', status);
731
+ }
732
+ return query.paginate(page, limit);
733
+ }
734
+ }
735
+ `);
736
+ addFile('app/services/LedgerService.ts', `import { DB } from '../../database/connection.js';
737
+
738
+ export interface JournalEntry {
739
+ l_id: number;
740
+ for_l_id?: number;
741
+ dr_amt: number;
742
+ cr_amt: number;
743
+ notes: string;
744
+ }
745
+
746
+ export class LedgerService {
747
+ public async postDoubleEntryVoucher(
748
+ vhId: number | string,
749
+ vhNo: string,
750
+ vhDate: string,
751
+ entries: JournalEntry[],
752
+ trxAdapter?: any
753
+ ): Promise<void> {
754
+ const totalDr = entries.reduce((sum, e) => sum + Number(e.dr_amt || 0), 0);
755
+ const totalCr = entries.reduce((sum, e) => sum + Number(e.cr_amt || 0), 0);
756
+
757
+ if (Math.abs(totalDr - totalCr) > 0.01) {
758
+ throw new Error(\`Double-Entry Violation: Debit (\${totalDr}) does not equal Credit (\${totalCr})\`);
759
+ }
760
+
761
+ const runner = trxAdapter || DB;
762
+
763
+ for (const e of entries) {
764
+ await runner.execute(
765
+ \`INSERT INTO tbl_ledger_transactions (vh_id, vh_no, vh_date, l_id, for_l_id, type, notes, dr_amt, cr_amt, amount, entrytime)
766
+ VALUES (?, ?, ?, ?, ?, 'journal', ?, ?, ?, ?, NOW())\`,
767
+ [vhId, vhNo, vhDate, e.l_id, e.for_l_id || 0, e.notes, e.dr_amt, e.cr_amt, e.dr_amt || e.cr_amt]
768
+ );
769
+
770
+ // Update current balance on tbl_ledger
771
+ const delta = Number(e.dr_amt) - Number(e.cr_amt);
772
+ await runner.execute(
773
+ \`UPDATE tbl_ledger SET c_balance = c_balance + ? WHERE l_id = ?\`,
774
+ [delta, e.l_id]
775
+ );
776
+ }
777
+ }
778
+
779
+ /**
780
+ * 4-Leg Double-Entry GL posting for Goods Dispatched/Delivered:
781
+ * 1. Dr. Courier Clearing / Accounts Receivable
782
+ * 2. Cr. Sales Revenue
783
+ * 3. Dr. Cost of Goods Sold (COGS)
784
+ * 4. Cr. Inventory Asset
785
+ */
786
+ public async postDeliveryVoucher(saleId: number | string, totalSaleAmount: number, trxAdapter?: any): Promise<void> {
787
+ const vhDate = new Date().toISOString().slice(0, 10);
788
+ const vhNo = 'GL-DELIV-' + saleId;
789
+
790
+ // Approximate COGS at 60% of revenue for demonstration / standard margin
791
+ const cogsAmount = Math.round(totalSaleAmount * 0.6 * 100) / 100;
792
+
793
+ const entries: JournalEntry[] = [
794
+ // Leg 1: Dr. Courier Clearing (l_id: 4)
795
+ { l_id: 4, for_l_id: 5, dr_amt: totalSaleAmount, cr_amt: 0, notes: \`Courier Clearing for Sale #\${saleId}\` },
796
+ // Leg 2: Cr. Sales Revenue (l_id: 5)
797
+ { l_id: 5, for_l_id: 4, dr_amt: 0, cr_amt: totalSaleAmount, notes: \`Sales Revenue for Sale #\${saleId}\` },
798
+ // Leg 3: Dr. COGS (l_id: 6)
799
+ { l_id: 6, for_l_id: 3, dr_amt: cogsAmount, cr_amt: 0, notes: \`COGS for Sale #\${saleId}\` },
800
+ // Leg 4: Cr. Inventory Asset (l_id: 3)
801
+ { l_id: 3, for_l_id: 6, dr_amt: 0, cr_amt: cogsAmount, notes: \`Inventory Relief for Sale #\${saleId}\` },
802
+ ];
803
+
804
+ await this.postDoubleEntryVoucher(saleId, vhNo, vhDate, entries, trxAdapter);
805
+ }
806
+
807
+ public async getTrialBalance() {
808
+ const rows = await DB.query<any>(\`
809
+ SELECT
810
+ l.l_id, l.l_name, g.type as account_type,
811
+ COALESCE(SUM(t.dr_amt), 0) as total_debit,
812
+ COALESCE(SUM(t.cr_amt), 0) as total_credit,
813
+ (COALESCE(SUM(t.dr_amt), 0) - COALESCE(SUM(t.cr_amt), 0)) as net_balance
814
+ FROM tbl_ledger l
815
+ LEFT JOIN tbl_ledger_group g ON l.h_id = g.h_id
816
+ LEFT JOIN tbl_ledger_transactions t ON l.l_id = t.l_id
817
+ GROUP BY l.l_id, l.l_name, g.type
818
+ ORDER BY g.type, l.l_name
819
+ \`);
820
+
821
+ const grandDebit = rows.reduce((s, r) => s + Number(r.total_debit), 0);
822
+ const grandCredit = rows.reduce((s, r) => s + Number(r.total_credit), 0);
823
+ const isBalanced = Math.abs(grandDebit - grandCredit) < 0.01;
824
+
825
+ return { accounts: rows, grandDebit, grandCredit, isBalanced };
826
+ }
827
+
828
+ public async getBalanceSheet() {
829
+ const tb = await this.getTrialBalance();
830
+ const assets = tb.accounts.filter(a => a.account_type === 'Asset');
831
+ const liabilities = tb.accounts.filter(a => a.account_type === 'Liability');
832
+ const equity = tb.accounts.filter(a => a.account_type === 'Equity');
833
+
834
+ const totalAssets = assets.reduce((s, a) => s + Number(a.net_balance), 0);
835
+ const totalLiabilities = liabilities.reduce((s, a) => s + Number(a.net_balance), 0);
836
+ const totalEquity = equity.reduce((s, a) => s + Number(a.net_balance), 0);
837
+
838
+ return {
839
+ assets,
840
+ liabilities,
841
+ equity,
842
+ totalAssets,
843
+ totalLiabilitiesAndEquity: totalLiabilities + totalEquity,
844
+ isBalanced: Math.abs(totalAssets - (totalLiabilities + totalEquity)) < 0.01
845
+ };
846
+ }
847
+
848
+ public async auditIntegrityCheck() {
849
+ const result = await DB.query<any>(\`
850
+ SELECT
851
+ vh_no,
852
+ SUM(dr_amt) as total_dr,
853
+ SUM(cr_amt) as total_cr,
854
+ ABS(SUM(dr_amt) - SUM(cr_amt)) as diff
855
+ FROM tbl_ledger_transactions
856
+ GROUP BY vh_no
857
+ HAVING diff > 0.01
858
+ \`);
859
+ return {
860
+ isClean: result.length === 0,
861
+ violatingVouchers: result,
862
+ checkedAt: new Date().toISOString()
863
+ };
864
+ }
865
+ }
866
+ `);
867
+ addFile('app/services/CourierService.ts', `import { DB } from '../../database/connection.js';
868
+ import { OnlineOrder } from '../models/OnlineOrder.js';
869
+
870
+ export class CourierService {
871
+ public async handleWebhook(payload: {
872
+ consignment_id?: string;
873
+ order_id?: string | number;
874
+ status: string;
875
+ collected_amount?: number;
876
+ delivery_fee?: number;
877
+ }) {
878
+ return DB.transaction(async (trx) => {
879
+ const order = payload.order_id
880
+ ? await OnlineOrder.find(payload.order_id, trx)
881
+ : await OnlineOrder.where('consignment_no', payload.consignment_id).first(trx);
882
+
883
+ if (!order) {
884
+ throw new Error(\`Order not found for courier webhook callback\`);
885
+ }
886
+
887
+ const courierStatus = payload.status.toLowerCase();
888
+ let orderStatus = order.get('status');
889
+
890
+ if (courierStatus.includes('delivered') || courierStatus === 'success') {
891
+ orderStatus = 'delivered';
892
+ } else if (courierStatus.includes('cancel') || courierStatus.includes('return')) {
893
+ orderStatus = 'returned';
894
+ }
895
+
896
+ await trx.execute(
897
+ \`UPDATE tbl_online_orders
898
+ SET courier_status = ?, status = ?, courier_delivery_fee = ?, updated_at = NOW()
899
+ WHERE id = ?\`,
900
+ [courierStatus, orderStatus, payload.delivery_fee || 0, order.get('id')]
901
+ );
902
+
903
+ return { success: true, order_id: order.get('id'), order_status: orderStatus };
904
+ });
905
+ }
906
+ }
907
+ `);
908
+ // 9. Controllers
909
+ addFile('app/controllers/DashboardController.ts', `import type { AeroContext } from 'aerojs';
910
+ import { DB } from '../../database/connection.js';
911
+
912
+ export class DashboardController {
913
+ public async index(ctx: AeroContext): Promise<void> {
914
+ const ordersCount = await DB.table('tbl_online_orders').count();
915
+ const productsCount = await DB.table('tbl_product').count();
916
+ const totalSalesRow = await DB.query<any>('SELECT COALESCE(SUM(net_amount), 0) as total FROM tbl_sale_head');
917
+ const recentOrders = await DB.table('tbl_online_orders').orderBy('id', 'DESC').limit(5);
918
+
919
+ const stats = {
920
+ totalOrders: Number(ordersCount),
921
+ totalProducts: Number(productsCount),
922
+ totalRevenue: Number(totalSalesRow[0]?.total || 0),
923
+ recentOrders,
924
+ };
925
+
926
+ if (ctx.req.get('X-Inertia') || ctx.req.path === '/dashboard' || ctx.req.path === '/') {
927
+ await ctx.inertia.render('Dashboard', stats);
928
+ } else {
929
+ ctx.status(200).json(stats);
930
+ }
931
+ }
932
+ }
933
+ `);
934
+ addFile('app/controllers/ProductController.ts', `import type { AeroContext } from 'aerojs';
935
+ import { ProductService } from '../services/ProductService.js';
936
+
937
+ export class ProductController {
938
+ private productService = new ProductService();
939
+
940
+ public async index(ctx: AeroContext): Promise<void> {
941
+ const page = parseInt((ctx.req.query.page as string) || '1', 10);
942
+ const search = (ctx.req.query.search as string) || '';
943
+ const products = await this.productService.paginate(page, 20, search);
944
+
945
+ if (ctx.req.get('X-Inertia') || ctx.req.path === '/products') {
946
+ await ctx.inertia.render('Products/Index', { products });
947
+ } else {
948
+ ctx.status(200).json({ success: true, data: products });
949
+ }
950
+ }
951
+
952
+ public async show(ctx: AeroContext): Promise<void> {
953
+ const id = ctx.req.params.id;
954
+ const product = await this.productService.findById(id);
955
+ if (!product) {
956
+ ctx.status(404).json({ error: 'Product not found' });
957
+ return;
958
+ }
959
+ ctx.status(200).json({ success: true, data: product });
960
+ }
961
+ }
962
+ `);
963
+ addFile('app/controllers/LandingPageController.ts', `import type { AeroContext } from 'aerojs';
964
+ import { ProductService } from '../services/ProductService.js';
965
+
966
+ export class LandingPageController {
967
+ private productService = new ProductService();
968
+
969
+ public async show(ctx: AeroContext): Promise<void> {
970
+ const slug = ctx.req.params.slug;
971
+ const result = await this.productService.findBySlug(slug);
972
+ if (!result.product) {
973
+ ctx.status(404).html('<h1>Product Not Found</h1>');
974
+ return;
975
+ }
976
+
977
+ if (ctx.req.get('X-Inertia') || !ctx.req.path.startsWith('/api')) {
978
+ await ctx.inertia.render('LandingPage', {
979
+ product: result.product,
980
+ landing: result.landing,
981
+ });
982
+ } else {
983
+ ctx.status(200).json({ success: true, data: result });
984
+ }
985
+ }
986
+ }
987
+ `);
988
+ addFile('app/controllers/OrderController.ts', `import type { AeroContext } from 'aerojs';
989
+ import { OrderService } from '../services/OrderService.js';
990
+
991
+ export class OrderController {
992
+ private orderService = new OrderService();
993
+
994
+ public async index(ctx: AeroContext): Promise<void> {
995
+ const page = parseInt((ctx.req.query.page as string) || '1', 10);
996
+ const status = ctx.req.query.status as string;
997
+ const orders = await this.orderService.paginate(page, 20, status);
998
+
999
+ if (ctx.req.get('X-Inertia') || ctx.req.path === '/orders') {
1000
+ await ctx.inertia.render('Orders/Index', { orders });
1001
+ } else {
1002
+ ctx.status(200).json({ success: true, data: orders });
1003
+ }
1004
+ }
1005
+
1006
+ public async store(ctx: AeroContext): Promise<void> {
1007
+ const payload = ctx.body as any;
1008
+ try {
1009
+ const order = await this.orderService.createOrder(payload);
1010
+ ctx.status(201).json({ success: true, data: order });
1011
+ } catch (err: any) {
1012
+ ctx.status(422).json({ error: err.message });
1013
+ }
1014
+ }
1015
+
1016
+ public async confirm(ctx: AeroContext): Promise<void> {
1017
+ const id = ctx.req.params.id;
1018
+ try {
1019
+ const result = await this.orderService.confirmOrder(id);
1020
+ ctx.status(200).json(result);
1021
+ } catch (err: any) {
1022
+ ctx.status(400).json({ error: err.message });
1023
+ }
1024
+ }
1025
+ }
1026
+ `);
1027
+ addFile('app/controllers/LedgerController.ts', `import type { AeroContext } from 'aerojs';
1028
+ import { LedgerService } from '../services/LedgerService.js';
1029
+
1030
+ export class LedgerController {
1031
+ private ledgerService = new LedgerService();
1032
+
1033
+ public async trialBalance(ctx: AeroContext): Promise<void> {
1034
+ const tb = await this.ledgerService.getTrialBalance();
1035
+ if (ctx.req.get('X-Inertia') || ctx.req.path === '/ledger') {
1036
+ await ctx.inertia.render('Ledger/Index', { trialBalance: tb });
1037
+ } else {
1038
+ ctx.status(200).json({ success: true, data: tb });
1039
+ }
1040
+ }
1041
+
1042
+ public async balanceSheet(ctx: AeroContext): Promise<void> {
1043
+ const bs = await this.ledgerService.getBalanceSheet();
1044
+ ctx.status(200).json({ success: true, data: bs });
1045
+ }
1046
+
1047
+ public async audit(ctx: AeroContext): Promise<void> {
1048
+ const check = await this.ledgerService.auditIntegrityCheck();
1049
+ ctx.status(200).json({ success: true, data: check });
1050
+ }
1051
+ }
1052
+ `);
1053
+ addFile('app/controllers/CourierWebhookController.ts', `import type { AeroContext } from 'aerojs';
1054
+ import { CourierService } from '../services/CourierService.js';
1055
+
1056
+ export class CourierWebhookController {
1057
+ private courierService = new CourierService();
1058
+
1059
+ public async handle(ctx: AeroContext): Promise<void> {
1060
+ const body = ctx.body as any;
1061
+ try {
1062
+ const result = await this.courierService.handleWebhook(body);
1063
+ ctx.status(200).json(result);
1064
+ } catch (err: any) {
1065
+ ctx.status(400).json({ error: err.message });
1066
+ }
1067
+ }
1068
+ }
1069
+ `);
1070
+ addFile('app/controllers/AuthController.ts', `import type { AeroContext } from 'aerojs';
1071
+ import { DB } from '../../database/connection.js';
1072
+
1073
+ export class AuthController {
1074
+ public async login(ctx: AeroContext): Promise<void> {
1075
+ const { email, password } = (ctx.body as any) || {};
1076
+ // Demo admin authentication
1077
+ if (email === 'admin@nursery.dev' && password === 'admin123') {
1078
+ ctx.status(200).json({
1079
+ success: true,
1080
+ token: 'demo-jwt-token-for-admin-erp',
1081
+ user: { id: 1, name: 'Managing Director', role: 'admin', email },
1082
+ });
1083
+ return;
1084
+ }
1085
+ ctx.status(401).json({ error: 'Invalid email or password' });
1086
+ }
1087
+
1088
+ public async me(ctx: AeroContext): Promise<void> {
1089
+ ctx.status(200).json({
1090
+ success: true,
1091
+ user: { id: 1, name: 'Managing Director', role: 'admin', email: 'admin@nursery.dev' },
1092
+ });
1093
+ }
1094
+ }
1095
+ `);
1096
+ // 10. Routes
1097
+ addFile('routes/api.ts', `import type { Router } from 'aerojs';
1098
+ import { ProductController } from '../app/controllers/ProductController.js';
1099
+ import { OrderController } from '../app/controllers/OrderController.js';
1100
+ import { LedgerController } from '../app/controllers/LedgerController.js';
1101
+ import { CourierWebhookController } from '../app/controllers/CourierWebhookController.js';
1102
+
1103
+ export function registerApiRoutes(router: Router): void {
1104
+ router.group('/api/v1', (api) => {
1105
+ // Products
1106
+ api.get('/products', [ProductController, 'index']);
1107
+ api.get('/products/:id', [ProductController, 'show']);
1108
+
1109
+ // Orders
1110
+ api.get('/orders', [OrderController, 'index']);
1111
+ api.post('/orders', [OrderController, 'store']);
1112
+ api.post('/orders/:id/confirm', [OrderController, 'confirm']);
1113
+
1114
+ // Accounting & GL
1115
+ api.get('/ledger/trial-balance', [LedgerController, 'trialBalance']);
1116
+ api.get('/ledger/balance-sheet', [LedgerController, 'balanceSheet']);
1117
+ api.get('/ledger/audit', [LedgerController, 'audit']);
1118
+
1119
+ // 3PL Couriers
1120
+ api.post('/webhooks/courier', [CourierWebhookController, 'handle']);
1121
+ });
1122
+ }
1123
+ `);
1124
+ addFile('routes/web.ts', `import type { Router } from 'aerojs';
1125
+ import { DashboardController } from '../app/controllers/DashboardController.js';
1126
+ import { ProductController } from '../app/controllers/ProductController.js';
1127
+ import { OrderController } from '../app/controllers/OrderController.js';
1128
+ import { LedgerController } from '../app/controllers/LedgerController.js';
1129
+ import { LandingPageController } from '../app/controllers/LandingPageController.js';
1130
+
1131
+ export function registerWebRoutes(router: Router): void {
1132
+ router.get('/', [DashboardController, 'index']);
1133
+ router.get('/dashboard', [DashboardController, 'index']);
1134
+ router.get('/products', [ProductController, 'index']);
1135
+ router.get('/orders', [OrderController, 'index']);
1136
+ router.get('/ledger', [LedgerController, 'trialBalance']);
1137
+ router.get('/p/:slug', [LandingPageController, 'show']);
1138
+ }
1139
+ `);
1140
+ addFile('routes/auth.ts', `import type { Router } from 'aerojs';
1141
+ import { AuthController } from '../app/controllers/AuthController.js';
1142
+
1143
+ export function registerAuthRoutes(router: Router): void {
1144
+ router.group('/auth', (auth) => {
1145
+ auth.post('/login', [AuthController, 'login']);
1146
+ auth.get('/me', [AuthController, 'me']);
1147
+ });
1148
+ }
1149
+ `);
1150
+ // 11. Frontend - Vue 3 & Inertia
1151
+ addFile('resources/js/app.ts', `import { createApp, h, type DefineComponent } from 'vue';
1152
+ import { createInertiaApp } from '@inertiajs/vue3';
1153
+ import '../css/app.css';
1154
+
1155
+ createInertiaApp({
1156
+ resolve: (name: string) => {
1157
+ const pages = (import.meta as any).glob('./Pages/**/*.vue', { eager: true });
1158
+ const page = pages[\`./Pages/\${name}.vue\`];
1159
+ if (!page) {
1160
+ throw new Error(\`Page component "\${name}" not found.\`);
1161
+ }
1162
+ return page.default as DefineComponent;
1163
+ },
1164
+ setup({ el, App, props, plugin }) {
1165
+ createApp({ render: () => h(App, props) })
1166
+ .use(plugin)
1167
+ .mount(el);
1168
+ },
1169
+ });
1170
+ `);
1171
+ addFile('resources/css/app.css', `:root {
1172
+ --bg-main: #0b1329;
1173
+ --bg-card: rgba(18, 28, 56, 0.85);
1174
+ --border-color: rgba(255, 255, 255, 0.1);
1175
+ --accent-green: #10b981;
1176
+ --accent-blue: #3b82f6;
1177
+ --text-main: #f3f4f6;
1178
+ --text-muted: #9ca3af;
1179
+ }
1180
+
1181
+ body {
1182
+ margin: 0;
1183
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1184
+ background-color: var(--bg-main);
1185
+ color: var(--text-main);
1186
+ }
1187
+
1188
+ .erp-card {
1189
+ background: var(--bg-card);
1190
+ border: 1px solid var(--border-color);
1191
+ border-radius: 12px;
1192
+ backdrop-filter: blur(16px);
1193
+ padding: 20px;
1194
+ }
1195
+ `);
1196
+ addFile('resources/js/Pages/Dashboard.vue', `<template>
1197
+ <div class="min-h-screen bg-slate-950 text-slate-100 p-6">
1198
+ <div class="max-w-7xl mx-auto space-y-6">
1199
+ <!-- Header -->
1200
+ <div class="flex items-center justify-between pb-4 border-b border-slate-800">
1201
+ <div>
1202
+ <h1 class="text-3xl font-extrabold bg-gradient-to-r from-emerald-400 to-teal-400 bg-clip-text text-transparent">
1203
+ Nursery & Omnichannel E-Commerce ERP
1204
+ </h1>
1205
+ <p class="text-sm text-slate-400 mt-1">Real-Time Enterprise Executive Dashboard</p>
1206
+ </div>
1207
+ <div class="flex gap-3">
1208
+ <a href="/products" class="px-4 py-2 bg-slate-800 hover:bg-slate-700 rounded-lg text-sm font-medium transition">Catalog</a>
1209
+ <a href="/orders" class="px-4 py-2 bg-emerald-600 hover:bg-emerald-500 rounded-lg text-sm font-medium text-white transition">Orders</a>
1210
+ <a href="/ledger" class="px-4 py-2 bg-blue-600 hover:bg-blue-500 rounded-lg text-sm font-medium text-white transition">Ledger</a>
1211
+ </div>
1212
+ </div>
1213
+
1214
+ <!-- Stats Grid -->
1215
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
1216
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 shadow-xl">
1217
+ <span class="text-xs uppercase tracking-wider text-slate-400">Total Revenue</span>
1218
+ <div class="text-3xl font-bold text-emerald-400 mt-2">৳ {{ totalRevenue.toLocaleString() }}</div>
1219
+ </div>
1220
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 shadow-xl">
1221
+ <span class="text-xs uppercase tracking-wider text-slate-400">Total Orders</span>
1222
+ <div class="text-3xl font-bold text-blue-400 mt-2">{{ totalOrders }}</div>
1223
+ </div>
1224
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 shadow-xl">
1225
+ <span class="text-xs uppercase tracking-wider text-slate-400">Active Plants & SKUs</span>
1226
+ <div class="text-3xl font-bold text-teal-400 mt-2">{{ totalProducts }}</div>
1227
+ </div>
1228
+ </div>
1229
+
1230
+ <!-- Recent Orders Table -->
1231
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 shadow-xl space-y-4">
1232
+ <h2 class="text-lg font-bold text-slate-200">Recent Omnichannel Orders</h2>
1233
+ <div class="overflow-x-auto">
1234
+ <table class="w-full text-left text-sm">
1235
+ <thead class="text-xs text-slate-400 uppercase bg-slate-800/50">
1236
+ <tr>
1237
+ <th class="p-3">Order No</th>
1238
+ <th class="p-3">Customer</th>
1239
+ <th class="p-3">District</th>
1240
+ <th class="p-3">Amount</th>
1241
+ <th class="p-3">Status</th>
1242
+ </tr>
1243
+ </thead>
1244
+ <tbody>
1245
+ <tr v-for="order in recentOrders" :key="order.id" class="border-b border-slate-800/50 hover:bg-slate-800/30">
1246
+ <td class="p-3 font-mono text-emerald-400">{{ order.order_no }}</td>
1247
+ <td class="p-3">{{ order.customer_name }}</td>
1248
+ <td class="p-3">{{ order.delivery_district }}</td>
1249
+ <td class="p-3 font-semibold">৳ {{ order.total_amount }}</td>
1250
+ <td class="p-3">
1251
+ <span class="px-2.5 py-1 rounded-full text-xs font-semibold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
1252
+ {{ order.status }}
1253
+ </span>
1254
+ </td>
1255
+ </tr>
1256
+ <tr v-if="!recentOrders || recentOrders.length === 0">
1257
+ <td colspan="5" class="p-4 text-center text-slate-500">No orders placed yet.</td>
1258
+ </tr>
1259
+ </tbody>
1260
+ </table>
1261
+ </div>
1262
+ </div>
1263
+ </div>
1264
+ </div>
1265
+ </template>
1266
+
1267
+ <script setup>
1268
+ defineProps({
1269
+ totalRevenue: { type: Number, default: 0 },
1270
+ totalOrders: { type: Number, default: 0 },
1271
+ totalProducts: { type: Number, default: 0 },
1272
+ recentOrders: { type: Array, default: () => [] }
1273
+ });
1274
+ </script>
1275
+ `);
1276
+ addFile('resources/js/Pages/Products/Index.vue', `<template>
1277
+ <div class="min-h-screen bg-slate-950 text-slate-100 p-6">
1278
+ <div class="max-w-7xl mx-auto space-y-6">
1279
+ <div class="flex items-center justify-between pb-4 border-b border-slate-800">
1280
+ <div>
1281
+ <h1 class="text-2xl font-bold">Plant & Product Inventory</h1>
1282
+ <p class="text-xs text-slate-400 mt-1">Multi-UOM SPU/SKU Stock Management</p>
1283
+ </div>
1284
+ <a href="/dashboard" class="px-4 py-2 bg-slate-800 hover:bg-slate-700 rounded-lg text-sm">Back to Dashboard</a>
1285
+ </div>
1286
+
1287
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 overflow-x-auto">
1288
+ <table class="w-full text-left text-sm">
1289
+ <thead class="text-xs text-slate-400 uppercase bg-slate-800/50">
1290
+ <tr>
1291
+ <th class="p-3">ID</th>
1292
+ <th class="p-3">Product Name</th>
1293
+ <th class="p-3">Scientific Name</th>
1294
+ <th class="p-3">Sale Rate</th>
1295
+ <th class="p-3">Available Stock</th>
1296
+ </tr>
1297
+ </thead>
1298
+ <tbody>
1299
+ <tr v-for="p in products.data" :key="p.product_id" class="border-b border-slate-800/50">
1300
+ <td class="p-3 font-mono text-slate-400">#{{ p.product_id }}</td>
1301
+ <td class="p-3 font-semibold text-emerald-400">{{ p.product_name }}</td>
1302
+ <td class="p-3 italic text-slate-400">{{ p.scientific_name || '-' }}</td>
1303
+ <td class="p-3">৳ {{ p.sale_rate }}</td>
1304
+ <td class="p-3">
1305
+ <span :class="p.c_stock > 0 ? 'text-emerald-400 font-bold' : 'text-rose-400 font-bold'">
1306
+ {{ p.c_stock }} {{ p.base_uom || 'pcs' }}
1307
+ </span>
1308
+ </td>
1309
+ </tr>
1310
+ </tbody>
1311
+ </table>
1312
+ </div>
1313
+ </div>
1314
+ </div>
1315
+ </template>
1316
+
1317
+ <script setup>
1318
+ defineProps({
1319
+ products: { type: Object, default: () => ({ data: [] }) }
1320
+ });
1321
+ </script>
1322
+ `);
1323
+ addFile('resources/js/Pages/Orders/Index.vue', `<template>
1324
+ <div class="min-h-screen bg-slate-950 text-slate-100 p-6">
1325
+ <div class="max-w-7xl mx-auto space-y-6">
1326
+ <div class="flex items-center justify-between pb-4 border-b border-slate-800">
1327
+ <div>
1328
+ <h1 class="text-2xl font-bold">Omnichannel Orders Pipeline</h1>
1329
+ <p class="text-xs text-slate-400 mt-1">O2C Lifecycle & 3PL Courier Dispatch</p>
1330
+ </div>
1331
+ <a href="/dashboard" class="px-4 py-2 bg-slate-800 hover:bg-slate-700 rounded-lg text-sm">Back to Dashboard</a>
1332
+ </div>
1333
+
1334
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 overflow-x-auto">
1335
+ <table class="w-full text-left text-sm">
1336
+ <thead class="text-xs text-slate-400 uppercase bg-slate-800/50">
1337
+ <tr>
1338
+ <th class="p-3">Order No</th>
1339
+ <th class="p-3">Customer</th>
1340
+ <th class="p-3">Phone</th>
1341
+ <th class="p-3">Total Amount</th>
1342
+ <th class="p-3">Status</th>
1343
+ <th class="p-3">Action</th>
1344
+ </tr>
1345
+ </thead>
1346
+ <tbody>
1347
+ <tr v-for="order in orders.data" :key="order.id" class="border-b border-slate-800/50">
1348
+ <td class="p-3 font-mono text-emerald-400">{{ order.order_no }}</td>
1349
+ <td class="p-3 font-semibold">{{ order.customer_name }}</td>
1350
+ <td class="p-3 text-slate-400">{{ order.customer_phone }}</td>
1351
+ <td class="p-3">৳ {{ order.total_amount }}</td>
1352
+ <td class="p-3">
1353
+ <span class="px-2.5 py-1 rounded-full text-xs font-semibold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
1354
+ {{ order.status }}
1355
+ </span>
1356
+ </td>
1357
+ <td class="p-3">
1358
+ <button
1359
+ v-if="order.status === 'pending'"
1360
+ @click="confirmOrder(order.id)"
1361
+ class="px-3 py-1 bg-emerald-600 hover:bg-emerald-500 rounded text-xs text-white cursor-pointer"
1362
+ >
1363
+ Confirm & Dispatch
1364
+ </button>
1365
+ </td>
1366
+ </tr>
1367
+ </tbody>
1368
+ </table>
1369
+ </div>
1370
+ </div>
1371
+ </div>
1372
+ </template>
1373
+
1374
+ <script setup>
1375
+ defineProps({
1376
+ orders: { type: Object, default: () => ({ data: [] }) }
1377
+ });
1378
+
1379
+ const confirmOrder = async (id) => {
1380
+ if (!confirm('Confirm and dispatch order #' + id + '?')) return;
1381
+ const res = await fetch('/api/v1/orders/' + id + '/confirm', { method: 'POST' });
1382
+ if (res.ok) window.location.reload();
1383
+ };
1384
+ </script>
1385
+ `);
1386
+ addFile('resources/js/Pages/Ledger/Index.vue', `<template>
1387
+ <div class="min-h-screen bg-slate-950 text-slate-100 p-6">
1388
+ <div class="max-w-7xl mx-auto space-y-6">
1389
+ <div class="flex items-center justify-between pb-4 border-b border-slate-800">
1390
+ <div>
1391
+ <h1 class="text-2xl font-bold">General Ledger & Trial Balance</h1>
1392
+ <p class="text-xs text-slate-400 mt-1">Double-Entry Financial Accounting Control</p>
1393
+ </div>
1394
+ <a href="/dashboard" class="px-4 py-2 bg-slate-800 hover:bg-slate-700 rounded-lg text-sm">Back to Dashboard</a>
1395
+ </div>
1396
+
1397
+ <div class="p-6 rounded-2xl bg-slate-900/80 border border-slate-800 space-y-4">
1398
+ <div class="flex items-center justify-between">
1399
+ <h2 class="text-lg font-bold">Trial Balance Summary</h2>
1400
+ <span
1401
+ :class="trialBalance.isBalanced ? 'px-3 py-1 bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 rounded-full text-xs font-bold' : 'px-3 py-1 bg-rose-500/10 text-rose-400 border border-rose-500/20 rounded-full text-xs font-bold'"
1402
+ >
1403
+ {{ trialBalance.isBalanced ? '✓ Double-Entry In Balance' : '⚠ Out of Balance' }}
1404
+ </span>
1405
+ </div>
1406
+
1407
+ <div class="overflow-x-auto">
1408
+ <table class="w-full text-left text-sm">
1409
+ <thead class="text-xs text-slate-400 uppercase bg-slate-800/50">
1410
+ <tr>
1411
+ <th class="p-3">Account Name</th>
1412
+ <th class="p-3">Type</th>
1413
+ <th class="p-3 text-right">Debit (৳)</th>
1414
+ <th class="p-3 text-right">Credit (৳)</th>
1415
+ <th class="p-3 text-right">Net Balance (৳)</th>
1416
+ </tr>
1417
+ </thead>
1418
+ <tbody>
1419
+ <tr v-for="acc in trialBalance.accounts" :key="acc.l_id" class="border-b border-slate-800/50">
1420
+ <td class="p-3 font-semibold text-slate-200">{{ acc.l_name }}</td>
1421
+ <td class="p-3 text-slate-400">{{ acc.account_type }}</td>
1422
+ <td class="p-3 text-right font-mono">{{ Number(acc.total_debit).toFixed(2) }}</td>
1423
+ <td class="p-3 text-right font-mono">{{ Number(acc.total_credit).toFixed(2) }}</td>
1424
+ <td class="p-3 text-right font-mono font-bold" :class="acc.net_balance >= 0 ? 'text-emerald-400' : 'text-rose-400'">
1425
+ {{ Number(acc.net_balance).toFixed(2) }}
1426
+ </td>
1427
+ </tr>
1428
+ </tbody>
1429
+ <tfoot class="font-bold bg-slate-800/30">
1430
+ <tr>
1431
+ <td class="p-3" colspan="2">Grand Total</td>
1432
+ <td class="p-3 text-right font-mono text-emerald-400">{{ Number(trialBalance.grandDebit || 0).toFixed(2) }}</td>
1433
+ <td class="p-3 text-right font-mono text-emerald-400">{{ Number(trialBalance.grandCredit || 0).toFixed(2) }}</td>
1434
+ <td class="p-3 text-right font-mono text-emerald-400">0.00</td>
1435
+ </tr>
1436
+ </tfoot>
1437
+ </table>
1438
+ </div>
1439
+ </div>
1440
+ </div>
1441
+ </div>
1442
+ </template>
1443
+
1444
+ <script setup>
1445
+ defineProps({
1446
+ trialBalance: { type: Object, default: () => ({ accounts: [], isBalanced: true, grandDebit: 0, grandCredit: 0 }) }
1447
+ });
1448
+ </script>
1449
+ `);
1450
+ addFile('resources/js/Pages/LandingPage.vue', `<template>
1451
+ <div class="min-h-screen bg-slate-950 text-slate-100 flex flex-col items-center p-4 sm:p-6 font-sans">
1452
+ <div class="max-w-xl w-full bg-slate-900 border border-slate-800 rounded-3xl p-6 shadow-2xl space-y-6">
1453
+ <!-- Badge & Title -->
1454
+ <div class="text-center space-y-2">
1455
+ <span class="px-3 py-1 rounded-full text-xs font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
1456
+ 🌿 100% Authentic Nursery Plant
1457
+ </span>
1458
+ <h1 class="text-3xl font-extrabold text-slate-100">{{ product.product_name }}</h1>
1459
+ <p class="text-sm text-slate-400 italic">{{ product.scientific_name }}</p>
1460
+ </div>
1461
+
1462
+ <!-- Price & Stock -->
1463
+ <div class="flex items-center justify-between p-4 rounded-2xl bg-slate-950/60 border border-slate-800">
1464
+ <div>
1465
+ <span class="text-xs text-slate-400">Special Offer Price</span>
1466
+ <div class="text-2xl font-bold text-emerald-400">৳ {{ product.sale_rate }}</div>
1467
+ </div>
1468
+ <div class="text-right">
1469
+ <span class="text-xs text-slate-400">Availability</span>
1470
+ <div class="text-sm font-semibold text-teal-400">In Stock ({{ product.c_stock }})</div>
1471
+ </div>
1472
+ </div>
1473
+
1474
+ <!-- Fast Checkout Form -->
1475
+ <form @submit.prevent="submitOrder" class="space-y-4">
1476
+ <h3 class="font-bold text-slate-200">Delivery Information (Cash on Delivery)</h3>
1477
+ <div>
1478
+ <label class="text-xs text-slate-400">Your Full Name</label>
1479
+ <input v-model="form.customer_name" required class="w-full mt-1 p-3 bg-slate-950 border border-slate-800 rounded-xl text-sm" placeholder="e.g. Tanvir Ahmed" />
1480
+ </div>
1481
+ <div>
1482
+ <label class="text-xs text-slate-400">Mobile Phone</label>
1483
+ <input v-model="form.customer_phone" required class="w-full mt-1 p-3 bg-slate-950 border border-slate-800 rounded-xl text-sm" placeholder="e.g. 01700000000" />
1484
+ </div>
1485
+ <div>
1486
+ <label class="text-xs text-slate-400">Delivery District</label>
1487
+ <input v-model="form.delivery_district" required class="w-full mt-1 p-3 bg-slate-950 border border-slate-800 rounded-xl text-sm" placeholder="e.g. Dhaka" />
1488
+ </div>
1489
+ <div>
1490
+ <label class="text-xs text-slate-400">Full Address</label>
1491
+ <textarea v-model="form.customer_address" required rows="2" class="w-full mt-1 p-3 bg-slate-950 border border-slate-800 rounded-xl text-sm" placeholder="House, Road, Area..."></textarea>
1492
+ </div>
1493
+
1494
+ <button type="submit" :disabled="loading" class="w-full py-4 bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-500 hover:to-teal-500 text-white font-bold rounded-2xl shadow-lg shadow-emerald-600/30 transition cursor-pointer">
1495
+ {{ loading ? 'Placing Order...' : 'Confirm Cash on Delivery Order' }}
1496
+ </button>
1497
+ </form>
1498
+ </div>
1499
+ </div>
1500
+ </template>
1501
+
1502
+ <script setup>
1503
+ import { reactive, ref } from 'vue';
1504
+
1505
+ const props = defineProps({
1506
+ product: { type: Object, required: true },
1507
+ landing: { type: Object, default: null }
1508
+ });
1509
+
1510
+ const loading = ref(false);
1511
+ const form = reactive({
1512
+ customer_name: '',
1513
+ customer_phone: '',
1514
+ customer_address: '',
1515
+ delivery_district: 'Dhaka',
1516
+ items: [{ product_id: props.product.product_id, qty: 1 }]
1517
+ });
1518
+
1519
+ const submitOrder = async () => {
1520
+ loading.value = true;
1521
+ try {
1522
+ const res = await fetch('/api/v1/orders', {
1523
+ method: 'POST',
1524
+ headers: { 'Content-Type': 'application/json' },
1525
+ body: JSON.stringify(form)
1526
+ });
1527
+ const data = await res.json();
1528
+ if (res.ok) {
1529
+ alert('Order Placed Successfully! Order No: ' + data.data.order_no);
1530
+ window.location.href = '/dashboard';
1531
+ } else {
1532
+ alert('Error: ' + data.error);
1533
+ }
1534
+ } finally {
1535
+ loading.value = false;
1536
+ }
1537
+ };
1538
+ </script>
1539
+ `);
1540
+ // 12. NodeFlow-inspired CLI Toolkit (cli.ts)
1541
+ addFile('cli.ts', `import readline from 'readline';
1542
+ import { initDatabase, DB, closeDatabase } from './database/connection.js';
1543
+ import { seed } from './database/seed.js';
1544
+ import { LedgerService } from './app/services/LedgerService.js';
1545
+
1546
+ const rl = readline.createInterface({
1547
+ input: process.stdin,
1548
+ output: process.stdout,
1549
+ });
1550
+
1551
+ const colors = {
1552
+ reset: "\\x1b[0m",
1553
+ bold: "\\x1b[1m",
1554
+ green: "\\x1b[32m",
1555
+ yellow: "\\x1b[33m",
1556
+ blue: "\\x1b[34m",
1557
+ cyan: "\\x1b[36m",
1558
+ red: "\\x1b[31m",
1559
+ };
1560
+
1561
+ function printMenu() {
1562
+ console.log(colors.cyan + "\\n╔══════════════════════════════════════════════════════════╗" + colors.reset);
1563
+ console.log(colors.cyan + "║ " + colors.bold + "NURSERY & E-COMMERCE ERP CLI (AeroJS Powered)" + colors.cyan + " ║" + colors.reset);
1564
+ console.log(colors.cyan + "╚══════════════════════════════════════════════════════════╝" + colors.reset);
1565
+ console.log("1. " + colors.green + "View Database Tables & Status" + colors.reset);
1566
+ console.log("2. " + colors.green + "Run Database Seed (Default Chart of Accounts)" + colors.reset);
1567
+ console.log("3. " + colors.yellow + "Financial Integrity Audit (Validate Double-Entry Dr == Cr)" + colors.reset);
1568
+ console.log("4. " + colors.blue + "View Real-Time Trial Balance" + colors.reset);
1569
+ console.log("5. " + colors.cyan + "View Balance Sheet" + colors.reset);
1570
+ console.log("0. " + colors.red + "Exit" + colors.reset);
1571
+ rl.question(colors.yellow + "\\nSelect option: " + colors.reset, handleChoice);
1572
+ }
1573
+
1574
+ async function handleChoice(choice: string) {
1575
+ try {
1576
+ await initDatabase();
1577
+ switch (choice.trim()) {
1578
+ case '1': {
1579
+ const tables = await DB.query<any>('SHOW TABLES');
1580
+ console.log(colors.green + \`\\nTotal Active Database Tables: \${tables.length}\\n\` + colors.reset);
1581
+ for (const t of tables.slice(0, 10)) {
1582
+ console.log(' - ' + Object.values(t)[0]);
1583
+ }
1584
+ if (tables.length > 10) console.log(\` ... and \${tables.length - 10} more tables.\`);
1585
+ break;
1586
+ }
1587
+ case '2': {
1588
+ await seed();
1589
+ break;
1590
+ }
1591
+ case '3': {
1592
+ const service = new LedgerService();
1593
+ const audit = await service.auditIntegrityCheck();
1594
+ if (audit.isClean) {
1595
+ console.log(colors.green + "\\n✓ Financial Health Check Passed! All vouchers in exact balance." + colors.reset);
1596
+ } else {
1597
+ console.log(colors.red + \`\\n⚠ Double-entry violations found in \${audit.violatingVouchers.length} vouchers!\` + colors.reset);
1598
+ }
1599
+ break;
1600
+ }
1601
+ case '4': {
1602
+ const service = new LedgerService();
1603
+ const tb = await service.getTrialBalance();
1604
+ console.table(tb.accounts);
1605
+ console.log(colors.green + \`Grand Debit: ৳ \${tb.grandDebit} | Grand Credit: ৳ \${tb.grandCredit} | Balanced: \${tb.isBalanced}\` + colors.reset);
1606
+ break;
1607
+ }
1608
+ case '5': {
1609
+ const service = new LedgerService();
1610
+ const bs = await service.getBalanceSheet();
1611
+ console.log(colors.cyan + \`\\nTotal Assets: ৳ \${bs.totalAssets} | Total Liab & Equity: ৳ \${bs.totalLiabilitiesAndEquity}\\n\` + colors.reset);
1612
+ break;
1613
+ }
1614
+ case '0': {
1615
+ await closeDatabase();
1616
+ rl.close();
1617
+ process.exit(0);
1618
+ }
1619
+ default:
1620
+ console.log('Invalid choice.');
1621
+ }
1622
+ } catch (err: any) {
1623
+ console.error(colors.red + 'CLI Error: ' + err.message + colors.reset);
1624
+ } finally {
1625
+ printMenu();
1626
+ }
1627
+ }
1628
+
1629
+ printMenu();
1630
+ `);
1631
+ // 13. Server bootstrapper (server.ts)
1632
+ addFile('server.ts', `import { Aero, Router, vite } from 'aerojs';
1633
+ import { appConfig } from './config/app.js';
1634
+ import { initDatabase } from './database/connection.js';
1635
+ import { registerApiRoutes } from './routes/api.js';
1636
+ import { registerWebRoutes } from './routes/web.js';
1637
+ import { registerAuthRoutes } from './routes/auth.js';
1638
+
1639
+ async function bootstrap() {
1640
+ // Initialize MySQL Connection Pool
1641
+ await initDatabase();
1642
+ console.log('⚡ MySQL Database connection pool online.');
1643
+
1644
+ const app = new Aero();
1645
+
1646
+ // Next.js style interactive error dashboard & diagnostics
1647
+ app.useDiagnostics();
1648
+
1649
+ // Inertia.js protocol configuration with Vite integration
1650
+ app.useInertia({
1651
+ version: '1.0',
1652
+ rootView: \`<!DOCTYPE html>
1653
+ <html lang="en">
1654
+ <head>
1655
+ <meta charset="UTF-8">
1656
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1657
+ <title>Nursery ERP & Omnichannel E-Commerce</title>
1658
+ <script src="https://cdn.tailwindcss.com"></script>
1659
+ \${vite('resources/js/app.ts')}
1660
+ </head>
1661
+ <body class="bg-slate-950 text-slate-100">
1662
+ @inertia
1663
+ </body>
1664
+ </html>\`
1665
+ });
1666
+
1667
+ // Enable Zero-CDN OpenAPI 3.1 & Swagger Documentation
1668
+ app.useOpenAPI({
1669
+ title: 'Nursery ERP API',
1670
+ version: '1.0.0',
1671
+ description: 'High-Throughput Omnichannel Nursery & E-Commerce ERP API Specification',
1672
+ });
1673
+
1674
+ // Mount Application Routes
1675
+ const router = new Router();
1676
+ registerApiRoutes(router);
1677
+ registerWebRoutes(router);
1678
+ registerAuthRoutes(router);
1679
+
1680
+ app.router.mount('/', router);
1681
+
1682
+ const port = appConfig.port;
1683
+ app.listen(port, () => {
1684
+ console.log(\`============================================================\`);
1685
+ console.log(\`🚀 Nursery ERP Server online at http://localhost:\${port}\`);
1686
+ console.log(\`📖 Interactive Swagger API Docs at http://localhost:\${port}/api/docs\`);
1687
+ console.log(\`⚡ Vue 3 + Inertia Admin Dashboard at http://localhost:\${port}/dashboard\`);
1688
+ console.log(\`============================================================\`);
1689
+ });
1690
+ }
1691
+
1692
+ bootstrap().catch((err) => {
1693
+ console.error('Fatal Server Boot Error:', err);
1694
+ process.exit(1);
1695
+ });
1696
+ `);
1697
+ // 14. Vitest integration test
1698
+ addFile('tests/erp.test.ts', `import { describe, it, expect, beforeAll, afterAll } from 'vitest';
1699
+ import { initDatabase, closeDatabase } from '../database/connection.js';
1700
+ import { LedgerService } from '../app/services/LedgerService.js';
1701
+ import { ProductService } from '../app/services/ProductService.js';
1702
+
1703
+ describe('Nursery ERP Full-Stack Integration Suite', () => {
1704
+ beforeAll(async () => {
1705
+ await initDatabase();
1706
+ });
1707
+
1708
+ afterAll(async () => {
1709
+ await closeDatabase();
1710
+ });
1711
+
1712
+ it('ProductService fetches product catalog', async () => {
1713
+ const service = new ProductService();
1714
+ const result = await service.paginate(1, 5);
1715
+ expect(result).toBeDefined();
1716
+ expect(Array.isArray(result.data)).toBe(true);
1717
+ });
1718
+
1719
+ it('LedgerService double-entry audit verifies integrity', async () => {
1720
+ const service = new LedgerService();
1721
+ const audit = await service.auditIntegrityCheck();
1722
+ expect(audit).toBeDefined();
1723
+ expect(typeof audit.isClean).toBe('boolean');
1724
+ });
1725
+
1726
+ it('LedgerService generates trial balance and balance sheet', async () => {
1727
+ const service = new LedgerService();
1728
+ const tb = await service.getTrialBalance();
1729
+ expect(tb).toBeDefined();
1730
+ expect(Array.isArray(tb.accounts)).toBe(true);
1731
+
1732
+ const bs = await service.getBalanceSheet();
1733
+ expect(bs).toBeDefined();
1734
+ expect(Array.isArray(bs.assets)).toBe(true);
1735
+ });
1736
+ });
1737
+ `);
1738
+ // 15. README.md
1739
+ addFile('README.md', `# ${projectName}
1740
+
1741
+ Omnichannel E-Commerce & Nursery ERP full-stack application built with **AeroJS**, **Vue 3**, **Inertia.js**, and **MySQL**.
1742
+
1743
+ ## Quick Start
1744
+ \`\`\`bash
1745
+ # 1. Install dependencies
1746
+ npm install
1747
+
1748
+ # 2. Seed default Chart of Accounts
1749
+ npm run db:seed
1750
+
1751
+ # 3. Launch interactive CLI toolkit
1752
+ npm run cli
1753
+
1754
+ # 4. Start Development Server
1755
+ npm run dev
1756
+ \`\`\`
1757
+ `);
1758
+ return createdFiles;
1759
+ }
1760
+ //# sourceMappingURL=ecommerce-erp.js.map