expense.entity.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';
  2. import { z } from '@hono/zod-openapi';
  3. @Entity('expense')
  4. export class Expense {
  5. @PrimaryGeneratedColumn({ unsigned: true })
  6. id!: number;
  7. @Column({ name: 'expense_date', type: 'date' })
  8. expenseDate!: Date;
  9. @Column({ name: 'user_id', type: 'varchar', length: 50 })
  10. userId!: string;
  11. @Column({ name: 'type', type: 'varchar', length: 50 })
  12. type!: string;
  13. @Column({ name: 'amount', type: 'decimal', precision: 10, scale: 2 })
  14. amount!: number;
  15. @Column({ name: 'client_id', type: 'int', unsigned: true, nullable: true })
  16. clientId?: number;
  17. @Column({ name: 'project_id', type: 'varchar', length: 50, nullable: true })
  18. projectId?: string;
  19. @Column({ name: 'department', type: 'varchar', length: 100, nullable: true })
  20. department?: string;
  21. @Column({ name: 'description', type: 'text', nullable: true })
  22. description?: string;
  23. @Column({ name: 'status', type: 'varchar', length: 50 })
  24. status!: string;
  25. @Column({ name: 'approver', type: 'varchar', length: 50, nullable: true })
  26. approver?: string;
  27. @Column({ name: 'approve_date', type: 'date', nullable: true })
  28. approveDate?: Date;
  29. @Column({ name: 'reimbursement_date', type: 'date', nullable: true })
  30. reimbursementDate?: Date;
  31. @Column({ name: 'payment_method', type: 'varchar', length: 50, nullable: true })
  32. paymentMethod?: string;
  33. @Column({ name: 'invoice_number', type: 'varchar', length: 100, nullable: true })
  34. invoiceNumber?: string;
  35. @Column({ name: 'currency', type: 'varchar', length: 20, default: 'CNY' })
  36. currency!: string;
  37. @Column({ name: 'exchange_rate', type: 'decimal', precision: 10, scale: 4, default: 1 })
  38. exchangeRate!: number;
  39. @Column({ name: 'foreign_amount', type: 'decimal', precision: 10, scale: 2, nullable: true })
  40. foreignAmount?: number;
  41. @Column({ name: 'created_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
  42. createdAt!: Date;
  43. @Column({
  44. name: 'updated_at',
  45. type: 'timestamp',
  46. default: () => 'CURRENT_TIMESTAMP',
  47. onUpdate: 'CURRENT_TIMESTAMP'
  48. })
  49. updatedAt!: Date;
  50. }
  51. export const ExpenseSchema = z.object({
  52. id: z.number().int().positive().openapi({
  53. description: '费用记录ID',
  54. example: 1
  55. }),
  56. expenseDate: z.date().openapi({
  57. description: '费用发生日期',
  58. example: '2023-01-15'
  59. }),
  60. userId: z.string().max(50).openapi({
  61. description: '产生费用的用户ID',
  62. example: 'U1001'
  63. }),
  64. type: z.string().max(50).openapi({
  65. description: '费用类型',
  66. example: '差旅费'
  67. }),
  68. amount: z.number().multipleOf(0.01).openapi({
  69. description: '费用金额',
  70. example: 1500.50
  71. }),
  72. clientId: z.number().int().positive().nullable().openapi({
  73. description: '关联客户ID',
  74. example: 2001
  75. }),
  76. projectId: z.string().max(50).nullable().openapi({
  77. description: '关联项目ID',
  78. example: 'P3001'
  79. }),
  80. department: z.string().max(100).nullable().openapi({
  81. description: '产生费用的部门',
  82. example: '研发部'
  83. }),
  84. description: z.string().nullable().openapi({
  85. description: '费用描述',
  86. example: '参加技术研讨会差旅费'
  87. }),
  88. status: z.string().max(50).openapi({
  89. description: '费用状态:如审批中、已报销等',
  90. example: '已报销'
  91. }),
  92. approver: z.string().max(50).nullable().openapi({
  93. description: '费用审批人ID',
  94. example: 'U1002'
  95. }),
  96. approveDate: z.date().nullable().openapi({
  97. description: '费用审批日期',
  98. example: '2023-01-20'
  99. }),
  100. reimbursementDate: z.date().nullable().openapi({
  101. description: '费用报销日期',
  102. example: '2023-01-25'
  103. }),
  104. paymentMethod: z.string().max(50).nullable().openapi({
  105. description: '支付方式',
  106. example: '银行卡'
  107. }),
  108. invoiceNumber: z.string().max(100).nullable().openapi({
  109. description: '发票号码',
  110. example: 'INV20230125001'
  111. }),
  112. currency: z.string().max(20).openapi({
  113. description: '货币类型',
  114. example: 'CNY'
  115. }),
  116. exchangeRate: z.number().multipleOf(0.0001).openapi({
  117. description: '汇率',
  118. example: 1.0000
  119. }),
  120. foreignAmount: z.number().multipleOf(0.01).nullable().openapi({
  121. description: '外币金额',
  122. example: null
  123. }),
  124. createdAt: z.date().openapi({
  125. description: '创建时间',
  126. example: '2023-01-15T00:00:00Z'
  127. }),
  128. updatedAt: z.date().openapi({
  129. description: '更新时间',
  130. example: '2023-01-25T00:00:00Z'
  131. })
  132. });
  133. export const CreateExpenseDto = z.object({
  134. expenseDate: z.coerce.date().openapi({
  135. description: '费用发生日期',
  136. example: '2023-01-15'
  137. }),
  138. type: z.string().max(50).openapi({
  139. description: '费用类型',
  140. example: '差旅费'
  141. }),
  142. amount: z.coerce.number().multipleOf(0.01).openapi({
  143. description: '费用金额',
  144. example: 1500.50
  145. }),
  146. clientId: z.coerce.number().int().positive().nullable().optional().openapi({
  147. description: '关联客户ID',
  148. example: 2001
  149. }),
  150. projectId: z.string().max(50).nullable().optional().openapi({
  151. description: '关联项目ID',
  152. example: 'P3001'
  153. }),
  154. department: z.string().max(100).nullable().optional().openapi({
  155. description: '产生费用的部门',
  156. example: '研发部'
  157. }),
  158. description: z.string().nullable().optional().openapi({
  159. description: '费用描述',
  160. example: '参加技术研讨会差旅费'
  161. }),
  162. status: z.string().max(50).openapi({
  163. description: '费用状态:如审批中、已报销等',
  164. example: '审批中'
  165. }),
  166. approver: z.string().max(50).nullable().optional().openapi({
  167. description: '费用审批人ID',
  168. example: 'U1002'
  169. }),
  170. approveDate: z.coerce.date().nullable().optional().openapi({
  171. description: '费用审批日期',
  172. example: '2023-01-20'
  173. }),
  174. reimbursementDate: z.coerce.date().nullable().optional().openapi({
  175. description: '费用报销日期',
  176. example: '2023-01-25'
  177. }),
  178. paymentMethod: z.string().max(50).nullable().optional().openapi({
  179. description: '支付方式',
  180. example: '银行卡'
  181. }),
  182. invoiceNumber: z.string().max(100).nullable().optional().openapi({
  183. description: '发票号码',
  184. example: 'INV20230125001'
  185. }),
  186. currency: z.string().max(20).default('CNY').openapi({
  187. description: '货币类型',
  188. example: 'CNY'
  189. }),
  190. exchangeRate: z.coerce.number().multipleOf(0.0001).default(1).openapi({
  191. description: '汇率',
  192. example: 1.0000
  193. }),
  194. foreignAmount: z.coerce.number().multipleOf(0.01).nullable().optional().openapi({
  195. description: '外币金额',
  196. example: null
  197. })
  198. });
  199. export const UpdateExpenseDto = z.object({
  200. expenseDate: z.coerce.date().optional().openapi({
  201. description: '费用发生日期',
  202. example: '2023-01-15'
  203. }),
  204. type: z.string().max(50).optional().openapi({
  205. description: '费用类型',
  206. example: '差旅费'
  207. }),
  208. amount: z.coerce.number().multipleOf(0.01).optional().openapi({
  209. description: '费用金额',
  210. example: 1500.50
  211. }),
  212. clientId: z.coerce.number().int().positive().nullable().optional().openapi({
  213. description: '关联客户ID',
  214. example: 2001
  215. }),
  216. projectId: z.string().max(50).nullable().optional().openapi({
  217. description: '关联项目ID',
  218. example: 'P3001'
  219. }),
  220. department: z.string().max(100).nullable().optional().openapi({
  221. description: '产生费用的部门',
  222. example: '研发部'
  223. }),
  224. description: z.string().nullable().optional().openapi({
  225. description: '费用描述',
  226. example: '参加技术研讨会差旅费'
  227. }),
  228. status: z.string().max(50).optional().openapi({
  229. description: '费用状态:如审批中、已报销等',
  230. example: '已报销'
  231. }),
  232. approver: z.string().max(50).nullable().optional().openapi({
  233. description: '费用审批人ID',
  234. example: 'U1002'
  235. }),
  236. approveDate: z.coerce.date().nullable().optional().openapi({
  237. description: '费用审批日期',
  238. example: '2023-01-20'
  239. }),
  240. reimbursementDate: z.coerce.date().nullable().optional().openapi({
  241. description: '费用报销日期',
  242. example: '2023-01-25'
  243. }),
  244. paymentMethod: z.string().max(50).nullable().optional().openapi({
  245. description: '支付方式',
  246. example: '银行卡'
  247. }),
  248. invoiceNumber: z.string().max(100).nullable().optional().openapi({
  249. description: '发票号码',
  250. example: 'INV20230125001'
  251. }),
  252. currency: z.string().max(20).optional().openapi({
  253. description: '货币类型',
  254. example: 'CNY'
  255. }),
  256. exchangeRate: z.coerce.number().multipleOf(0.0001).optional().openapi({
  257. description: '汇率',
  258. example: 1.0000
  259. }),
  260. foreignAmount: z.coerce.number().multipleOf(0.01).nullable().optional().openapi({
  261. description: '外币金额',
  262. example: null
  263. })
  264. });