lingxingapi 1.1.4__py3-none-any.whl

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 (65) hide show
  1. lingxingapi/__init__.py +7 -0
  2. lingxingapi/ads/__init__.py +0 -0
  3. lingxingapi/ads/api.py +5946 -0
  4. lingxingapi/ads/param.py +192 -0
  5. lingxingapi/ads/route.py +134 -0
  6. lingxingapi/ads/schema.py +2615 -0
  7. lingxingapi/api.py +557 -0
  8. lingxingapi/base/__init__.py +0 -0
  9. lingxingapi/base/api.py +568 -0
  10. lingxingapi/base/param.py +59 -0
  11. lingxingapi/base/route.py +11 -0
  12. lingxingapi/base/schema.py +198 -0
  13. lingxingapi/basic/__init__.py +0 -0
  14. lingxingapi/basic/api.py +466 -0
  15. lingxingapi/basic/param.py +72 -0
  16. lingxingapi/basic/route.py +20 -0
  17. lingxingapi/basic/schema.py +218 -0
  18. lingxingapi/errors.py +152 -0
  19. lingxingapi/fba/__init__.py +0 -0
  20. lingxingapi/fba/api.py +1691 -0
  21. lingxingapi/fba/param.py +250 -0
  22. lingxingapi/fba/route.py +30 -0
  23. lingxingapi/fba/schema.py +987 -0
  24. lingxingapi/fields.py +50 -0
  25. lingxingapi/finance/__init__.py +0 -0
  26. lingxingapi/finance/api.py +3091 -0
  27. lingxingapi/finance/param.py +616 -0
  28. lingxingapi/finance/route.py +44 -0
  29. lingxingapi/finance/schema.py +1243 -0
  30. lingxingapi/product/__init__.py +0 -0
  31. lingxingapi/product/api.py +2643 -0
  32. lingxingapi/product/param.py +934 -0
  33. lingxingapi/product/route.py +49 -0
  34. lingxingapi/product/schema.py +1004 -0
  35. lingxingapi/purchase/__init__.py +0 -0
  36. lingxingapi/purchase/api.py +496 -0
  37. lingxingapi/purchase/param.py +126 -0
  38. lingxingapi/purchase/route.py +11 -0
  39. lingxingapi/purchase/schema.py +215 -0
  40. lingxingapi/sales/__init__.py +0 -0
  41. lingxingapi/sales/api.py +3200 -0
  42. lingxingapi/sales/param.py +723 -0
  43. lingxingapi/sales/route.py +70 -0
  44. lingxingapi/sales/schema.py +1718 -0
  45. lingxingapi/source/__init__.py +0 -0
  46. lingxingapi/source/api.py +1799 -0
  47. lingxingapi/source/param.py +176 -0
  48. lingxingapi/source/route.py +38 -0
  49. lingxingapi/source/schema.py +1011 -0
  50. lingxingapi/tools/__init__.py +0 -0
  51. lingxingapi/tools/api.py +291 -0
  52. lingxingapi/tools/param.py +73 -0
  53. lingxingapi/tools/route.py +8 -0
  54. lingxingapi/tools/schema.py +169 -0
  55. lingxingapi/utils.py +456 -0
  56. lingxingapi/warehourse/__init__.py +0 -0
  57. lingxingapi/warehourse/api.py +1778 -0
  58. lingxingapi/warehourse/param.py +506 -0
  59. lingxingapi/warehourse/route.py +28 -0
  60. lingxingapi/warehourse/schema.py +926 -0
  61. lingxingapi-1.1.4.dist-info/METADATA +73 -0
  62. lingxingapi-1.1.4.dist-info/RECORD +65 -0
  63. lingxingapi-1.1.4.dist-info/WHEEL +5 -0
  64. lingxingapi-1.1.4.dist-info/licenses/LICENSE +22 -0
  65. lingxingapi-1.1.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,934 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Optional
3
+ from pydantic import ValidationInfo, Field, field_validator
4
+ from lingxingapi import utils
5
+ from lingxingapi.base.param import Parameter, PageOffestAndLength
6
+ from lingxingapi.fields import (
7
+ NonEmptyStr,
8
+ CurrencyCode,
9
+ NonNegativeInt,
10
+ NonNegativeFloat,
11
+ )
12
+
13
+
14
+ # 产品 --------------------------------------------------------------------------------------------------------------------------
15
+ # . Products
16
+ class Products(PageOffestAndLength):
17
+ """产品列表查询参数"""
18
+
19
+ # 领星本地SKU列表
20
+ lskus: Optional[list] = Field(None, alias="sku_list")
21
+ # 领星本地SKU识别码
22
+ sku_identifiers: Optional[list] = Field(None, alias="sku_identifier")
23
+ # 产品更新开始时间, 左闭右开 (时间戳, 单位: 秒)
24
+ update_start_time: Optional[int] = Field(None, alias="update_time_start")
25
+ # 产品更新结束时间, 左闭右开 (时间戳, 单位: 秒)
26
+ update_end_time: Optional[int] = Field(None, alias="update_time_end")
27
+ # 产品创建开始时间, 左闭右开 (时间戳, 单位: 秒)
28
+ create_start_time: Optional[int] = Field(None, alias="create_time_start")
29
+ # 产品创建结束时间, 左闭右开 (时间戳, 单位: 秒)
30
+ create_end_time: Optional[int] = Field(None, alias="create_time_end")
31
+
32
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33
+ @field_validator("lskus", mode="before")
34
+ @classmethod
35
+ def _validate_lskus(cls, v) -> list[str] | None:
36
+ if v is None:
37
+ return None
38
+ return utils.validate_array_of_non_empty_str(v, "领星本地SKU lskus")
39
+
40
+ @field_validator("sku_identifiers", mode="before")
41
+ @classmethod
42
+ def _validate_sku_identifiers(cls, v) -> list[str] | None:
43
+ if v is None:
44
+ return None
45
+ return utils.validate_array_of_str(v, "领星SKU识别码 sku_identifiers")
46
+
47
+ @field_validator(
48
+ "update_start_time",
49
+ "update_end_time",
50
+ "create_start_time",
51
+ "create_end_time",
52
+ mode="before",
53
+ )
54
+ @classmethod
55
+ def _validate_time(cls, v: Optional[int], info: ValidationInfo) -> int | None:
56
+ if v is None:
57
+ return None
58
+ dt = utils.validate_datetime(v, False, "产品时间 %s" % info.field_name)
59
+ return int(dt.toseconds())
60
+
61
+
62
+ class EnableDisableProducts(Parameter):
63
+ """批量启用/禁用产品参数"""
64
+
65
+ # 产品起停用状态 (1: 启用, 2: 禁用)
66
+ status: str = Field(alias="batch_status")
67
+ # 领星本地产品ID列表 (Product.product_id)
68
+ product_ids: list
69
+
70
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
71
+ @field_validator("status", mode="before")
72
+ @classmethod
73
+ def _validate_status(cls, v: str) -> str:
74
+ if v in (1, "Enable", "1"):
75
+ return "Enable"
76
+ if v in (0, "Disable", "0"):
77
+ return "Disable"
78
+ raise ValueError("产品起停用 status 必须为 (0/Disable) 或 (1/Enable)")
79
+
80
+ @field_validator("product_ids", mode="before")
81
+ @classmethod
82
+ def _validate_product_ids(cls, v) -> list[int]:
83
+ return utils.validate_array_of_unsigned_int(v, "领星本地产品ID product_ids")
84
+
85
+
86
+ # . Product Details
87
+ class ProductDetails(Parameter):
88
+ """产品详情查询参数"""
89
+
90
+ # 领星本地SKU列表 (Product.lsku)
91
+ lskus: Optional[list] = Field(None, alias="skus")
92
+ # 领星本地SKU识别码列表 (Product.sku_identifier)
93
+ sku_identifiers: Optional[list] = Field(None, alias="sku_identifiers")
94
+ # 领星本地产品ID列表 (Product.product_id)
95
+ product_ids: Optional[list] = Field(None, alias="productIds")
96
+
97
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
98
+ @field_validator("lskus", mode="before")
99
+ @classmethod
100
+ def _validate_lskus(cls, v) -> list[str] | None:
101
+ if v is None:
102
+ return None
103
+ return utils.validate_array_of_non_empty_str(v, "领星本地SKU lskus")
104
+
105
+ @field_validator("sku_identifiers", mode="before")
106
+ @classmethod
107
+ def _validate_sku_identifiers(cls, v) -> list[str] | None:
108
+ if v is None:
109
+ return None
110
+ return utils.validate_array_of_str(v, "领星SKU识别码 sku_identifiers")
111
+
112
+ @field_validator("product_ids", mode="before")
113
+ @classmethod
114
+ def _validate_product_ids(cls, v) -> list[int] | None:
115
+ if v is None:
116
+ return None
117
+ return utils.validate_array_of_unsigned_int(v, "领星本地产品ID product_ids")
118
+
119
+
120
+ # . Edit Product
121
+ class EditProductImage(Parameter):
122
+ """编辑产品图片参数"""
123
+
124
+ # 图片链接
125
+ image_url: NonEmptyStr = Field(alias="pic_url")
126
+ # 是否为主图 (0: 否, 1: 是)
127
+ is_primary: NonNegativeInt
128
+
129
+
130
+ class EditProductBundleItem(Parameter):
131
+ """编辑产品捆绑参数"""
132
+
133
+ # 领星本地子产品SKU
134
+ lsku: NonEmptyStr = Field(alias="sku")
135
+ # 子产品数量
136
+ product_qty: NonNegativeInt = Field(alias="quantity")
137
+
138
+
139
+ class EditProductQuotePriceTeir(Parameter):
140
+ """编辑产品报价定价梯度参数"""
141
+
142
+ # fmt: off
143
+ # 最小订购量 (MOQ)
144
+ moq: int = Field(gt=0)
145
+ # 报价 (含税)
146
+ price_with_tax: NonNegativeFloat
147
+ # fmt: on
148
+
149
+
150
+ class EditProductQuotePricing(Parameter):
151
+ """编辑产品报价参数"""
152
+
153
+ # fmt: off
154
+ # 报价货币代码 (目前只支持CNY和USD)
155
+ currency_code: CurrencyCode = Field(alias="currency")
156
+ # 报价是否含税 (0: 否, 1: 是)
157
+ is_tax_inclusive: NonNegativeInt = Field(alias="is_tax")
158
+ # 报价税率 (百分比, 如 5% 则传 5)
159
+ tax_rate: NonNegativeFloat = 0
160
+ # 报价梯度 [原字段 'step_prices']
161
+ price_tiers: list = Field(alias="step_prices")
162
+ # fmt: on
163
+
164
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165
+ @field_validator("price_tiers", mode="before")
166
+ @classmethod
167
+ def _validate_price_tiers(cls, v) -> list[dict]:
168
+ if not isinstance(v, (list, tuple)):
169
+ return [EditProductQuotePriceTeir.model_validate_params(v)]
170
+ else:
171
+ return [EditProductQuotePriceTeir.model_validate_params(i) for i in v]
172
+
173
+
174
+ class EditProductSupplierQuote(Parameter):
175
+ """编辑产品供应商报价参数"""
176
+
177
+ # fmt: off
178
+ # 供应商ID (Supplier.supplier_id)
179
+ supplier_id: NonNegativeInt = Field(alias="erp_supplier_id")
180
+ # 是否是首选供应商 (0: 否, 1: 是)
181
+ is_primary: NonNegativeInt
182
+ # 供应商产品链接 (最多20个,没有则传空数组)
183
+ product_urls: Optional[list] = Field(None, alias="supplier_product_url")
184
+ # 报价备注
185
+ quote_note: Optional[str] = Field(None, alias="quote_remark")
186
+ # 报价列表
187
+ quotes: list
188
+ # fmt: on
189
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
190
+ @field_validator("product_urls", mode="before")
191
+ @classmethod
192
+ def _validate_product_urls(cls, v) -> list[str] | None:
193
+ if v is None:
194
+ return None
195
+ return utils.validate_array_of_str(v, "供应商产品链接 product_urls")
196
+
197
+ @field_validator("quotes", mode="before")
198
+ @classmethod
199
+ def _validate_quotes(cls, v) -> list[dict]:
200
+ if not isinstance(v, (list, tuple)):
201
+ return [EditProductQuotePricing.model_validate_params(v)]
202
+ else:
203
+ return [EditProductQuotePricing.model_validate_params(i) for i in v]
204
+
205
+
206
+ class EditCustomsDeclaration(Parameter):
207
+ """编辑产品报关申报参数"""
208
+
209
+ # fmt: off
210
+ # 报关申报品名 (出口国)
211
+ export_name: Optional[str] = Field(None, alias="customs_export_name")
212
+ # 报关申报品名 (进口国)
213
+ import_name: Optional[str] = Field(None, alias="customs_import_name")
214
+ # 报关申报单价 (进口国)
215
+ import_price: Optional[NonNegativeFloat] = Field(None, alias="customs_import_price")
216
+ # 报关申报单价货币代码 (进口国)
217
+ currency_code: Optional[str] = Field(None, alias="customs_import_price_currency")
218
+ # 报关申报产品单位
219
+ unit: Optional[str] = Field(None, alias="customs_declaration_unit")
220
+ # 报关申报产品规格
221
+ specification: Optional[str] = Field(None, alias="customs_declaration_spec")
222
+ # 报关申报产品原产地
223
+ country_of_origin: Optional[str] = Field(None, alias="customs_declaration_origin_produce")
224
+ # 报关申报内陆来源
225
+ source_from_inland: Optional[str] = Field(None, alias="customs_declaration_inlands_source")
226
+ # 报关申报免税
227
+ exemption: Optional[str] = Field(None, alias="customs_declaration_exempt")
228
+ # fmt: on
229
+
230
+
231
+ class EditCustomsClearance(Parameter):
232
+ """编辑产品报关清关参数"""
233
+
234
+ # fmt: off
235
+ # 清关内部编码
236
+ internal_code: Optional[str] = Field(None, alias="customs_clearance_internal_code")
237
+ # 清关产品材质
238
+ material: Optional[str] = Field(None, alias="customs_clearance_material")
239
+ # 清关产品用途
240
+ usage: Optional[str] = Field(None, alias="customs_clearance_usage")
241
+ # 清关是否享受优惠 (0: 未设置, 1: 不享惠, 2: 享惠, 3: 不确定)
242
+ preferential: Optional[NonNegativeInt] = Field(None, alias="customs_clearance_preferential")
243
+ # 清关品牌类型 (0: 未设置, 1: 无品牌, 2: 境内品牌[自主], 3: 境内品牌[收购], 4: 境外品牌[贴牌], 5: 境外品牌[其他])
244
+ brand_type: Optional[NonNegativeInt] = Field(None, alias="customs_clearance_brand_type")
245
+ # 清关产品型号
246
+ model: Optional[str] = Field(None, alias="customs_clearance_product_pattern")
247
+ # 清关产品图片链接
248
+ image_url: Optional[str] = Field(None, alias="customs_clearance_pic_url")
249
+ # 配货备注
250
+ allocation_note: Optional[str] = Field(None, alias="allocation_remark")
251
+ # 织造类型 (0: 未设置, 1: 针织, 2: 梭织)
252
+ fabric_type: Optional[NonNegativeInt] = Field(None, alias="weaving_mode")
253
+ # fmt: on
254
+
255
+
256
+ class EditProduct(Parameter):
257
+ """编辑产品参数"""
258
+
259
+ # fmt: off
260
+ # 领星本地SKU (Product.lsku)
261
+ lsku: NonEmptyStr = Field(alias="sku")
262
+ # 领星本地产品名称 (Product.product_name)
263
+ product_name: NonEmptyStr
264
+ # 领星本地SKU识别码
265
+ sku_identifier: Optional[str] = None
266
+ # 领星本地产品分类ID (当ID与名称同时存在时, ID优先)
267
+ category_id: Optional[NonNegativeInt] = None
268
+ # 领星本地产品分类名称
269
+ category_name: Optional[str] = Field(None, alias="category")
270
+ # 领星本地产品品牌ID (当ID与名称同时存在时, ID优先)
271
+ brand_id: Optional[NonNegativeInt] = None
272
+ # 领星本地产品品牌名称
273
+ brand_name: Optional[str] = Field(None, alias="brand")
274
+ # 产品型号
275
+ product_model: Optional[str] = Field(None, alias="model")
276
+ # 产品单位
277
+ product_unit: Optional[str] = Field(None, alias="unit")
278
+ # 产品描述
279
+ product_description: Optional[str] = Field(None, alias="description")
280
+ # 产品图片列表
281
+ product_images: Optional[list] = Field(None, alias="picture_list")
282
+ # 产品特殊属性列表 (1: 含电, 2: 纯电, 3: 液体, 4: 粉末, 5: 膏体, 6: 带磁)
283
+ product_special_attrs: Optional[list] = Field(None, alias="special_attr")
284
+ # 产品状态 (0: 停售, 1: 在售, 2: 开发中, 3: 清仓 | 默认: 1)
285
+ status: Optional[NonNegativeInt] = None
286
+ # 组合产品所包含的单品列表
287
+ bundle_items: Optional[list] = Field(None, alias="group_list")
288
+ # 是否自动计算组合产品采购价格 (0: 手动, 1: 自动 | 选择自动后, 组合产品采购价格为所包含单品成本的总计)
289
+ auto_bundle_purchase_price: Optional[NonNegativeInt] = Field(None, alias="is_related")
290
+ # 产品创建人ID (默认API账号ID)
291
+ product_creator_id: Optional[NonNegativeInt] = Field(None, alias="product_creator_uid")
292
+ # 产品开发者用户ID (当ID与名称同时存在时, ID优先)
293
+ product_developer_id: Optional[NonNegativeInt] = Field(None, alias="product_developer_uid")
294
+ # 产品开发者姓名
295
+ product_developer_name: Optional[str] = Field(None, alias="product_developer")
296
+ # 产品采购人ID (当ID与名称同时存在时, ID优先)
297
+ purchase_staff_id: Optional[NonNegativeInt] = Field(None, alias="cg_opt_uid")
298
+ # 产品采购人姓名
299
+ purchase_staff_name: Optional[str] = Field(None, alias="cg_opt_username")
300
+ # 采购交期 (单位: 天)
301
+ purchase_delivery_time: Optional[NonNegativeInt] = Field(None, alias="cg_delivery")
302
+ # 采购价格
303
+ purchase_price: Optional[NonNegativeFloat] = Field(None, alias="cg_price")
304
+ # 采购备注
305
+ purchase_note: Optional[str] = Field(None, alias="purchase_remark")
306
+ # 采购产品材质
307
+ product_material: Optional[str] = Field(None, alias="cg_product_material")
308
+ # 采购产品总重 (单位: G)
309
+ product_gross_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_product_gross_weight")
310
+ # 采购产品净重 (单位: G)
311
+ product_net_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_product_net_weight")
312
+ # 采购产品长度 (单位: CM)
313
+ product_length: Optional[NonNegativeFloat] = Field(None, alias="cg_product_length")
314
+ # 采购产品宽度 (单位: CM)
315
+ product_width: Optional[NonNegativeFloat] = Field(None, alias="cg_product_width")
316
+ # 采购产品高度 (单位: CM)
317
+ product_height: Optional[NonNegativeFloat] = Field(None, alias="cg_product_height")
318
+ # 采购包装长度 (单位: CM)
319
+ package_length: Optional[NonNegativeFloat] = Field(None, alias="cg_package_length")
320
+ # 采购包装宽度 (单位: CM)
321
+ package_width: Optional[NonNegativeFloat] = Field(None, alias="cg_package_width")
322
+ # 采购包装高度 (单位: CM)
323
+ package_height: Optional[NonNegativeFloat] = Field(None, alias="cg_package_height")
324
+ # 采购外箱重量 (单位: KG)
325
+ box_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_box_weight")
326
+ # 采购外箱长度 (单位: CM)
327
+ box_length: Optional[NonNegativeFloat] = Field(None, alias="cg_box_length")
328
+ # 采购外箱宽度 (单位: CM)
329
+ box_width : Optional[NonNegativeFloat] = Field(None, alias="cg_box_width")
330
+ # 采购外箱高度 (单位: CM)
331
+ box_height: Optional[NonNegativeFloat] = Field(None, alias="cg_box_height")
332
+ # 采购外箱数量
333
+ box_qty: Optional[NonNegativeInt] = Field(None, alias="cg_box_pcs")
334
+ # 供应商报价信息列表 (传空列表则清空产品供应商报价)
335
+ supplier_quotes: Optional[list] = Field(None, alias="supplier_quote")
336
+ # 报关申报品名 (出口国)
337
+ customs_export_name: Optional[str] = Field(None, alias="bg_customs_export_name")
338
+ # 报关申报HS编码 (出口国)
339
+ customs_export_hs_code: Optional[str] = Field(None, alias="bg_export_hs_code")
340
+ # 报关申报品名 (进口国)
341
+ customs_import_name: Optional[str] = Field(None, alias="bg_customs_import_name")
342
+ # 报关申报单价 (进口国)
343
+ customs_import_price: Optional[NonNegativeFloat] = Field(None, alias="bg_customs_import_price")
344
+ # 报关申报单价货币代码 (进口国)
345
+ customs_import_currency_code: Optional[str] = Field(None, alias="currency")
346
+ # 报关信息
347
+ customs_declaration: Optional[dict] = Field(None, alias="declaration")
348
+ # 清关信息
349
+ customs_clearance: Optional[dict] = Field(None, alias="clearance")
350
+ # 产品负责人ID列表
351
+ operator_ids: Optional[list] = Field(None, alias="product_duty_uids")
352
+ # 产品负责人ID的更新模式 (0: 覆盖, 1: 追加 | 默认: 1)
353
+ operator_update_mode: Optional[NonNegativeInt] = Field(None, alias="is_append_product_duty")
354
+ # fmt: on
355
+
356
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
357
+ @field_validator("product_images", mode="before")
358
+ @classmethod
359
+ def _validate_product_images(cls, v) -> list[dict] | None:
360
+ if v is None:
361
+ return None
362
+ elif not isinstance(v, (list, tuple)):
363
+ return [EditProductImage.model_validate_params(v)]
364
+ else:
365
+ return [EditProductImage.model_validate_params(i) for i in v]
366
+
367
+ @field_validator("product_special_attrs", "operator_ids", mode="before")
368
+ @classmethod
369
+ def _validate_unsigned_int_array(cls, v, info: ValidationInfo) -> list[int] | None:
370
+ if v is None:
371
+ return None
372
+ if not v:
373
+ return []
374
+ return utils.validate_array_of_unsigned_int(v, info.field_name)
375
+
376
+ @field_validator("bundle_items", mode="before")
377
+ @classmethod
378
+ def _validate_bundle_items(cls, v) -> list[dict] | None:
379
+ if v is None:
380
+ return None
381
+ elif not isinstance(v, (list, tuple)):
382
+ return [EditProductBundleItem.model_validate_params(v)]
383
+ else:
384
+ return [EditProductBundleItem.model_validate_params(i) for i in v]
385
+
386
+ @field_validator("supplier_quotes", mode="before")
387
+ @classmethod
388
+ def _validate_supplier_quotes(cls, v) -> list[dict] | None:
389
+ if v is None:
390
+ return None
391
+ elif not isinstance(v, (list, tuple)):
392
+ return [EditProductSupplierQuote.model_validate_params(v)]
393
+ else:
394
+ return [EditProductSupplierQuote.model_validate_params(i) for i in v]
395
+
396
+ @field_validator("customs_declaration", mode="before")
397
+ @classmethod
398
+ def _validate_customs_declaration(cls, v) -> dict | None:
399
+ if v is None:
400
+ return None
401
+ return EditCustomsDeclaration.model_validate_params(v)
402
+
403
+ @field_validator("customs_clearance", mode="before")
404
+ @classmethod
405
+ def _validate_customs_clearance(cls, v) -> dict | None:
406
+ if v is None:
407
+ return None
408
+ return EditCustomsClearance.model_validate_params(v)
409
+
410
+
411
+ # . Spu Products
412
+ class SpuProductDetail(Parameter):
413
+ """SPU产品详情查询参数"""
414
+
415
+ # 领星SPU多属性产品ID (SpuProduct.spu_id)
416
+ spu_id: NonNegativeInt = Field(alias="ps_id")
417
+ # 领星SPU多属性产品编码
418
+ spu: NonEmptyStr
419
+
420
+
421
+ # . Edit Spu Product
422
+ class EditSpuProductItemAttribute(Parameter):
423
+ """编辑SPU多属性产品子项属性参数"""
424
+
425
+ # 属性ID
426
+ attr_id: NonNegativeInt = Field(alias="pa_id")
427
+ # 属性名称
428
+ attr_value_id: NonNegativeInt = Field(alias="pai_id")
429
+
430
+
431
+ class EditSpuProductItem(Parameter):
432
+ """编辑SPU多属性产品子项参数"""
433
+
434
+ # 子产品本地SKU
435
+ lsku: NonEmptyStr = Field(alias="sku")
436
+ # 子产品本地名称
437
+ product_name: Optional[NonEmptyStr] = None
438
+ # 子产品图片列表
439
+ images: Optional[list] = Field(None, alias="picture_list")
440
+ # 子产品属性
441
+ attributes: list = Field(alias="attribute")
442
+
443
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
444
+ @field_validator("attributes", mode="before")
445
+ @classmethod
446
+ def _validate_attributes(cls, v) -> list[dict]:
447
+ if not isinstance(v, (list, tuple)):
448
+ return [EditSpuProductItemAttribute.model_validate_params(v)]
449
+ else:
450
+ return [EditSpuProductItemAttribute.model_validate_params(i) for i in v]
451
+
452
+ @field_validator("images", mode="before")
453
+ @classmethod
454
+ def _validate_images(cls, v) -> list[dict] | None:
455
+ if v is None:
456
+ return None
457
+ elif not isinstance(v, (list, tuple)):
458
+ return [EditProductImage.model_validate_params(v)]
459
+ else:
460
+ return [EditProductImage.model_validate_params(i) for i in v]
461
+
462
+
463
+ class EditSpuProductPurchaseInfo(Parameter):
464
+ """编辑SPU多属性产品采购信息参数"""
465
+
466
+ # fmt: off
467
+ # 产品采购人用户ID (Account.user_id)
468
+ purchase_staff_id: Optional[NonNegativeInt] = Field(None, alias="cg_uid")
469
+ # 采购交期 (单位: 天)
470
+ purchase_delivery_time: Optional[NonNegativeInt] = Field(None, alias="cg_delivery")
471
+ # 采购备注
472
+ purchase_note: Optional[str] = Field(None, alias="purchase_remark")
473
+ # 采购产品材质
474
+ product_material: Optional[str] = Field(None, alias="cg_product_material")
475
+ # 采购产品总重 (单位: G)
476
+ product_gross_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_product_gross_weight")
477
+ # 采购产品净重 (单位: G)
478
+ product_net_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_product_net_weight")
479
+ # 采购产品长度 (单位: CM)
480
+ product_length: Optional[NonNegativeFloat] = Field(None, alias="cg_product_length")
481
+ # 采购产品宽度 (单位: CM)
482
+ product_width: Optional[NonNegativeFloat] = Field(None, alias="cg_product_width")
483
+ # 采购产品高度 (单位: CM)
484
+ product_height: Optional[NonNegativeFloat] = Field(None, alias="cg_product_height")
485
+ # 采购包装长度 (单位: CM)
486
+ package_length: Optional[NonNegativeFloat] = Field(None, alias="cg_package_length")
487
+ # 采购包装宽度 (单位: CM)
488
+ package_width: Optional[NonNegativeFloat] = Field(None, alias="cg_package_width")
489
+ # 采购包装高度 (单位: CM)
490
+ package_height: Optional[NonNegativeFloat] = Field(None, alias="cg_package_height")
491
+ # 采购外箱重量 (单位: KG)
492
+ box_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_box_weight")
493
+ # 采购外箱长度 (单位: CM)
494
+ box_length: Optional[NonNegativeFloat] = Field(None, alias="cg_box_length")
495
+ # 采购外箱宽度 (单位: CM)
496
+ box_width: Optional[NonNegativeFloat] = Field(None, alias="cg_box_width")
497
+ # 采购外箱高度 (单位: CM)
498
+ box_height: Optional[NonNegativeFloat] = Field(None, alias="cg_box_height")
499
+ # 采购外箱数量
500
+ box_qty: Optional[NonNegativeInt] = Field(None, alias="cg_box_pcs")
501
+ # fmt: on
502
+
503
+
504
+ class EditSpuProductCustomsBaseInfo(Parameter):
505
+ """编辑SPU多属性产品报关信息参数"""
506
+
507
+ # 报关申报HS编码 (出口国)
508
+ customs_export_hs_code: Optional[str] = Field(None, alias="bg_export_hs_code")
509
+ # 产品特殊属性 (1: 含电, 2: 纯电, 3: 液体, 4: 粉末, 5: 膏体, 6: 带磁)
510
+ special_attrs: Optional[list] = Field(None, alias="special_attr")
511
+
512
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
513
+ @field_validator("special_attrs", mode="before")
514
+ @classmethod
515
+ def _validate_special_attrs(cls, v) -> list[int] | None:
516
+ if v is None:
517
+ return None
518
+ if not v:
519
+ return []
520
+ return utils.validate_array_of_unsigned_int(v, "产品特殊属性 special_attrs")
521
+
522
+
523
+ class EditSpuProductCustomsInfo(Parameter):
524
+ """编辑SPU多属性产品报关信息参数"""
525
+
526
+ # 基础产品信息
527
+ base_info: Optional[dict] = Field(None, alias="base")
528
+ # 海关报关信息
529
+ declaration: Optional[dict] = None
530
+ # 海关清关信息
531
+ clearance: Optional[dict] = None
532
+
533
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
534
+ @field_validator("base_info", mode="before")
535
+ @classmethod
536
+ def _validate_base_info(cls, v) -> dict | None:
537
+ if v is None:
538
+ return None
539
+ return EditSpuProductCustomsBaseInfo.model_validate_params(v)
540
+
541
+ @field_validator("declaration", mode="before")
542
+ @classmethod
543
+ def _validate_declaration(cls, v) -> dict | None:
544
+ if v is None:
545
+ return None
546
+ return EditCustomsDeclaration.model_validate_params(v)
547
+
548
+ @field_validator("clearance", mode="before")
549
+ @classmethod
550
+ def _validate_clearance(cls, v) -> dict | None:
551
+ if v is None:
552
+ return None
553
+ return EditCustomsClearance.model_validate_params(v)
554
+
555
+
556
+ class EditSpuProduct(Parameter):
557
+ """编辑SPU多属性产品参数"""
558
+
559
+ # fmt: off
560
+ # 领星SPU多属性产品编码
561
+ spu: NonEmptyStr
562
+ # 领星SPU多属性产品名称
563
+ spu_name: NonEmptyStr
564
+ # 子产品列表
565
+ items: list = Field(alias="sku_list")
566
+ # 领星本地产品分类ID
567
+ category_id: Optional[NonNegativeInt] = Field(None, alias="cid")
568
+ # 领星本地产品品牌ID
569
+ brand_id: Optional[NonNegativeInt] = Field(None, alias="bid")
570
+ # 产品型号
571
+ product_model: Optional[str] = Field(None, alias="model")
572
+ # 产品单位
573
+ product_unit: Optional[str] = Field(None, alias="unit")
574
+ # 产品描述
575
+ product_description: Optional[str] = Field(None, alias="description")
576
+ # 产品状态 (0: 停售, 1: 在售, 2: 开发中, 3: 清仓 | 默认: 1)
577
+ status: Optional[NonNegativeInt] = None
578
+ # 产品创建人ID (默认API账号ID)
579
+ product_creator_id: Optional[NonNegativeInt] = Field(None, alias="create_uid")
580
+ # 产品开发者用户ID
581
+ product_developer_id: Optional[NonNegativeInt] = Field(None, alias="developer_uid")
582
+ # 产品负责人ID列表
583
+ operator_ids: Optional[list] = Field(None, alias="product_duty_uids")
584
+ # 是否应用SPU多属性产品基础信息至新生成的SKU (0: 否, 1: 是)
585
+ apply_to_new_skus: Optional[NonNegativeInt] = Field(None, alias="use_spu_template")
586
+ # 采购信息
587
+ purchase_info: Optional[dict] = None
588
+ # 海关申报信息
589
+ customs_info: Optional[dict] = Field(None, alias="logistics")
590
+ # fmt: on
591
+
592
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
593
+ @field_validator("items", mode="before")
594
+ @classmethod
595
+ def _validate_items(cls, v) -> list[dict]:
596
+ if not isinstance(v, (list, tuple)):
597
+ return [EditSpuProductItem.model_validate_params(v)]
598
+ else:
599
+ return [EditSpuProductItem.model_validate_params(i) for i in v]
600
+
601
+ @field_validator("operator_ids", mode="before")
602
+ @classmethod
603
+ def _validate_unsigned_int_array(cls, v, info: ValidationInfo) -> list[int] | None:
604
+ if v is None:
605
+ return None
606
+ if not v:
607
+ return []
608
+ return utils.validate_array_of_unsigned_int(v, info.field_name)
609
+
610
+ @field_validator("purchase_info", mode="before")
611
+ @classmethod
612
+ def _validate_purchase_info(cls, v) -> dict | None:
613
+ if v is None:
614
+ return None
615
+ return EditSpuProductPurchaseInfo.model_validate_params(v)
616
+
617
+ @field_validator("customs_info", mode="before")
618
+ @classmethod
619
+ def _validate_customs_info(cls, v) -> dict | None:
620
+ if v is None:
621
+ return None
622
+ return EditSpuProductCustomsInfo.model_validate_params(v)
623
+
624
+
625
+ # . Edit Bundle Product
626
+ class EditBundleProductItem(Parameter):
627
+ """编辑组合产品子项参数"""
628
+
629
+ # 子产品SKU
630
+ lsku: Optional[NonEmptyStr] = Field(None, alias="sku")
631
+ # 子产品捆绑数量
632
+ bundle_qty: Optional[NonNegativeInt] = Field(None, alias="quantity")
633
+ # 子产品费用比例
634
+ cost_ratio: Optional[NonNegativeFloat] = None
635
+
636
+
637
+ class EditBundleProduct(Parameter):
638
+ """编辑组合产品参数"""
639
+
640
+ # fmt: off
641
+ # 捆绑产品本地SKU
642
+ bundle_sku: NonEmptyStr = Field(alias="sku")
643
+ # 捆绑产品本地名称
644
+ bundle_name: NonEmptyStr = Field(alias="product_name")
645
+ # 领星本地产品分类ID (当ID与名称同时存在时, ID优先)
646
+ category_id: Optional[NonNegativeInt] = None
647
+ # 领星本地产品分类名称
648
+ category_name: Optional[str] = Field(None, alias="category")
649
+ # 领星本地产品品牌ID (当ID与名称同时存在时, ID优先)
650
+ brand_id: Optional[NonNegativeInt] = None
651
+ # 领星本地产品品牌名称
652
+ brand_name: Optional[str] = Field(None, alias="brand")
653
+ # 产品型号
654
+ product_model: Optional[str] = Field(None, alias="model")
655
+ # 产品单位
656
+ product_unit: Optional[str] = Field(None, alias="unit")
657
+ # 产品描述
658
+ product_description: Optional[str] = Field(None, alias="description")
659
+ # 产品图片列表
660
+ product_images: Optional[list] = Field(None, alias="picture_list")
661
+ # 产品创建人ID (默认API账号ID)
662
+ product_creator_id: Optional[NonNegativeInt] = Field(None, alias="product_creator_uid")
663
+ # 产品开发者用户ID (当ID与名称同时存在时, ID优先)
664
+ product_developer_id: Optional[NonNegativeInt] = Field(None, alias="product_developer_uid")
665
+ # 产品开发者姓名
666
+ product_developer_name: Optional[str] = Field(None, alias="product_developer")
667
+ # 产品负责人ID列表
668
+ operator_ids: Optional[list] = Field(None, alias="product_duty_uids")
669
+ # 产品负责人ID的更新模式 (0: 覆盖, 1: 追加 | 默认: 1)
670
+ operator_update_mode: Optional[NonNegativeInt] = Field(None, alias="is_append_product_duty")
671
+ # 组合产品所包含的单品列表
672
+ items: Optional[list] = Field(None, alias="group_list")
673
+ # fmt: on
674
+
675
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
676
+ @field_validator("product_images", mode="before")
677
+ @classmethod
678
+ def _validate_product_images(cls, v) -> list[dict] | None:
679
+ if v is None:
680
+ return None
681
+ elif not isinstance(v, (list, tuple)):
682
+ return [EditProductImage.model_validate_params(v)]
683
+ else:
684
+ return [EditProductImage.model_validate_params(i) for i in v]
685
+
686
+ @field_validator("operator_ids", mode="before")
687
+ @classmethod
688
+ def _validate_operator_ids(cls, v, info: ValidationInfo) -> list[int] | None:
689
+ if v is None:
690
+ return None
691
+ if not v:
692
+ return []
693
+ return utils.validate_array_of_unsigned_int(v, info.field_name)
694
+
695
+ @field_validator("items", mode="before")
696
+ @classmethod
697
+ def _validate_items(cls, v) -> list[dict] | None:
698
+ if v is None:
699
+ return None
700
+ elif not isinstance(v, (list, tuple)):
701
+ return [EditBundleProductItem.model_validate_params(v)]
702
+ else:
703
+ return [EditBundleProductItem.model_validate_params(i) for i in v]
704
+
705
+
706
+ # . Edit Auxiliary Materials
707
+ class EditAuxiliaryMaterial(Parameter):
708
+ """编辑辅助材料参数"""
709
+
710
+ # fmt: off
711
+ # 辅料SKU
712
+ aux_sku: NonEmptyStr = Field(alias="sku")
713
+ # 辅料名称
714
+ aux_name: NonEmptyStr = Field(alias="product_name")
715
+ # 辅料净重
716
+ aux_net_weight: Optional[NonNegativeFloat] = Field(None, alias="cg_product_net_weight")
717
+ # 辅料长度
718
+ aux_length: Optional[NonNegativeFloat] = Field(None, alias="cg_product_length")
719
+ # 辅料宽度
720
+ aux_width: Optional[NonNegativeFloat] = Field(None, alias="cg_product_width")
721
+ # 辅料高度
722
+ aux_height: Optional[NonNegativeFloat] = Field(None, alias="cg_product_height")
723
+ # 辅料备注
724
+ aux_note: Optional[str] = Field(None, alias="remark")
725
+ # 辅料采购价格
726
+ purchase_price: Optional[NonNegativeFloat] = Field(None, alias="cg_price")
727
+ # 供应商报价信息列表
728
+ supplier_quotes: Optional[list] = Field(None, alias="supplier_quote")
729
+ # fmt: on
730
+
731
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
732
+ @field_validator("supplier_quotes", mode="before")
733
+ @classmethod
734
+ def _validate_supplier_quotes(cls, v) -> list[dict] | None:
735
+ if v is None:
736
+ return None
737
+ elif not isinstance(v, (list, tuple)):
738
+ return [EditProductSupplierQuote.model_validate_params(v)]
739
+ else:
740
+ return [EditProductSupplierQuote.model_validate_params(i) for i in v]
741
+
742
+
743
+ # . Product Codes
744
+ class CreateProductCode(Parameter):
745
+ # 编码类型
746
+ code_type: NonEmptyStr
747
+ # 编码列表
748
+ codes: list = Field(alias="commodity_codes")
749
+
750
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
751
+ @field_validator("codes", mode="before")
752
+ @classmethod
753
+ def _validate_codes(cls, v) -> list[str]:
754
+ return utils.validate_array_of_non_empty_str(v, "UPC编码列表 codes")
755
+
756
+
757
+ # . Product Global Tags
758
+ class CreateProductGlobalTag(Parameter):
759
+ """创建产品全局标签参数"""
760
+
761
+ # 全局标签名称
762
+ tag_name: NonEmptyStr = Field(alias="label")
763
+
764
+
765
+ # . Product Tags
766
+ class MapOfProductAndTags(Parameter):
767
+ """设置产品标签映射参数"""
768
+
769
+ # 领星本地产品SKU (Product.lsku)
770
+ lsku: NonEmptyStr = Field(alias="sku")
771
+ # 产品全局标签名称 (ProductGlobalTag.tag_name)
772
+ tags: list = Field(alias="label_list")
773
+
774
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
775
+ @field_validator("tags", mode="before")
776
+ @classmethod
777
+ def _validate_tags(cls, v) -> list[str]:
778
+ if not v:
779
+ return []
780
+ return utils.validate_array_of_non_empty_str(v, "产品标签列表 tags")
781
+
782
+
783
+ class SetProductTag(Parameter):
784
+ """设置产品全局标签参数"""
785
+
786
+ # 设置模式 (1: 追加, 2: 覆盖)
787
+ mode: NonNegativeInt = Field(alias="type")
788
+ # 产品和标签映射列表
789
+ product_tags: list = Field(alias="detail_list")
790
+
791
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
792
+ @field_validator("product_tags", mode="before")
793
+ @classmethod
794
+ def _validate_product_tags(cls, v) -> list[dict]:
795
+ if not isinstance(v, (list, tuple)):
796
+ return [MapOfProductAndTags.model_validate_params(v)]
797
+ else:
798
+ return [MapOfProductAndTags.model_validate_params(i) for i in v]
799
+
800
+
801
+ class UnsetProductTag(Parameter):
802
+ """删除产品全局标签参数"""
803
+
804
+ # 设置模式 (1: 删除SKU指定的标签, 2: 删除SKU全部的标签)
805
+ mode: NonNegativeInt = Field(alias="type")
806
+ # 产品和标签映射列表
807
+ product_tags: list = Field(alias="detail_list")
808
+
809
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
810
+ @field_validator("product_tags", mode="before")
811
+ @classmethod
812
+ def _validate_product_tags(cls, v) -> list[dict]:
813
+ if not isinstance(v, (list, tuple)):
814
+ return [MapOfProductAndTags.model_validate_params(v)]
815
+ else:
816
+ return [MapOfProductAndTags.model_validate_params(i) for i in v]
817
+
818
+
819
+ # . Product Global Attributes
820
+ class EditGlobalAttributeValue(Parameter):
821
+ """更新产品全局属性值参数"""
822
+
823
+ # 产品属性值
824
+ attr_value: NonEmptyStr
825
+ # 产品属性值ID
826
+ attr_value_id: Optional[NonNegativeInt] = Field(None, alias="pai_id")
827
+
828
+
829
+ class EditProductGlobalAttribute(Parameter):
830
+ """更新产品全局属性参数"""
831
+
832
+ # 产品属性ID
833
+ attr_id: Optional[NonNegativeInt] = Field(None, alias="pa_id")
834
+ # 产品属性名称
835
+ attr_name: NonEmptyStr
836
+ # 产品属性值列表
837
+ attr_values: list
838
+
839
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
840
+ @field_validator("attr_values", mode="before")
841
+ @classmethod
842
+ def _validate_attr_values(cls, v) -> list[dict]:
843
+ if not isinstance(v, (list, tuple)):
844
+ v = [v]
845
+
846
+ res: list = []
847
+ for i in v:
848
+ if isinstance(i, str):
849
+ i = {"attr_value": i}
850
+ res.append(EditGlobalAttributeValue.model_validate_params(i))
851
+ return res
852
+
853
+
854
+ # . Product Brands
855
+ class EditProductBrand(Parameter):
856
+ """更新产品品牌参数"""
857
+
858
+ # 领星本地品牌ID
859
+ brand_id: Optional[NonNegativeInt] = Field(None, alias="id")
860
+ # 领星本地品牌名称
861
+ brand_name: NonEmptyStr = Field(alias="title")
862
+ # 领星本地品牌编码
863
+ brand_code: Optional[NonEmptyStr] = None
864
+
865
+
866
+ class EditProductBrands(Parameter):
867
+ """批量更新产品品牌参数"""
868
+
869
+ # 领星本地品牌列表
870
+ brands: list = Field(alias="data")
871
+
872
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
873
+ @field_validator("brands", mode="before")
874
+ @classmethod
875
+ def _validate_brands(cls, v) -> list[dict]:
876
+ if not isinstance(v, (list, tuple)):
877
+ v = [v]
878
+
879
+ res: list = []
880
+ for i in v:
881
+ if isinstance(i, str):
882
+ i = {"title": i}
883
+ res.append(EditProductBrand.model_validate_params(i))
884
+ return res
885
+
886
+
887
+ # . Product Categories
888
+ class ProductCategories(PageOffestAndLength):
889
+ """产品分类查询参数"""
890
+
891
+ # 领星本地分类ID列表
892
+ category_ids: Optional[list] = Field(None, alias="ids")
893
+
894
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
895
+ @field_validator("category_ids", mode="before")
896
+ @classmethod
897
+ def _validate_category_ids(cls, v) -> list[int] | None:
898
+ if not v:
899
+ return None
900
+ return utils.validate_array_of_unsigned_int(v, "领星本地分类ID category_ids")
901
+
902
+
903
+ class EditProductCategory(Parameter):
904
+ """更新产品分类参数"""
905
+
906
+ # 领星本地产品分类ID
907
+ category_id: Optional[NonNegativeInt] = Field(None, alias="id")
908
+ # 领星本地产品分类名称
909
+ category_name: NonEmptyStr = Field(alias="title")
910
+ # 领星本地产品分类编码
911
+ category_code: str = ""
912
+ # 父分类ID [原字段 'parent_cid']
913
+ parent_category_id: Optional[NonNegativeInt] = Field(None, alias="parent_cid")
914
+
915
+
916
+ class EditProductCategories(Parameter):
917
+ """批量更新产品分类参数"""
918
+
919
+ # 领星本地产品分类列表
920
+ categories: list = Field(alias="data")
921
+
922
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
923
+ @field_validator("categories", mode="before")
924
+ @classmethod
925
+ def _validate_categories(cls, v) -> list[dict]:
926
+ if not isinstance(v, (list, tuple)):
927
+ v = [v]
928
+
929
+ res: list = []
930
+ for i in v:
931
+ if isinstance(i, str):
932
+ i = {"title": i, "category_code": ""}
933
+ res.append(EditProductCategory.model_validate_params(i))
934
+ return res