@@ -55,69 +55,188 @@ export interface ProductStatus {
5555
5656export type CommercialStatus = 'pending' | 'approved' | 'rejected' ;
5757
58- // PARA REMOVER FUTURAMENTE APÓS UPDATE
59-
60- export interface ProductPricePayload {
61- price : number ;
62- promotional_price ?: number | null ;
63- promotion_start ?: string | null ;
64- promotion_end ?: string | null ;
65- }
66-
67- export interface ProductInventoryPayload {
68- warehouse_id : string | null ;
69-
70- quantity : number ;
71-
72- minimum_quantity ?: number ;
73- maximum_quantity ?: number | null ;
74-
75- allow_backorder ?: boolean ;
76- }
77-
58+ /**
59+ * Payload utilizado para atualização parcial de um produto.
60+ *
61+ * Os campos são opcionais porque o backend utiliza a regra
62+ * `sometimes` no UpdateProductRequest.
63+ *
64+ * Importante:
65+ * - `undefined` significa que o campo não será enviado.
66+ * - `null` é utilizado somente nos campos em que o backend
67+ * permite explicitamente valor nulo.
68+ * - Arrays vazios devem ser enviados quando a intenção for
69+ * persistir uma coleção vazia.
70+ */
7871export interface UpdateProductPayload {
79- category_id ?: string | null ;
80- manufacturer_id ?: string | null ;
72+ category_id ?: string ;
73+ manufacturer_id ?: string ;
8174 part_origin_id ?: string | null ;
8275 unit_id ?: string | null ;
76+ status_id ?: string ;
77+
78+ oem_code_ids ?: string [ ] ;
79+ product_code_ids ?: string [ ] ;
80+ equivalent_product_ids ?: string [ ] ;
81+ application_ids ?: string [ ] ;
82+ supplier_ids ?: string [ ] ;
83+ tag_ids ?: string [ ] ;
84+ icon ?: string | null ;
8385
84- internal_code ?: string | null ;
86+ internal_code ?: string ;
8587 barcode ?: string | null ;
86- name ?: string | null ;
87- slug ?: string | null ;
8888
89- icon ?: string | null ;
90- image ?: string | null ;
89+ name ?: string ;
90+ slug ?: string ;
9191
9292 short_description ?: string | null ;
93- description ?: string | null ;
93+ description ?: string ;
9494
95- weight ?: number | null ;
96- height ?: number | null ;
97- width ?: number | null ;
98- length ?: number | null ;
95+ weight ?: number ;
96+ height ?: number ;
97+ width ?: number ;
98+ length ?: number ;
9999
100- unit ?: string | null ;
101-
102- active ?: boolean ;
103100 featured ?: boolean ;
101+ active ?: boolean ;
104102
105103 price ?: UpdateProductPricePayload ;
106104
107- inventory ?: UpdateProductInventoryPayload ;
105+ inventories ?: UpdateProductInventoryPayload [ ] ;
108106}
109107
108+ /**
109+ * Dados comerciais utilizados na atualização do produto.
110+ */
110111export interface UpdateProductPricePayload {
111- price ?: number | null ;
112+ price ?: number ;
112113 promotional_price ?: number | null ;
113114 promotion_start ?: string | null ;
114115 promotion_end ?: string | null ;
115116}
116117
118+ /**
119+ * Estoque de um produto por depósito.
120+ *
121+ * O backend aceita múltiplos registros em `inventories`.
122+ */
117123export interface UpdateProductInventoryPayload {
118- warehouse_id ?: string | null ;
119- quantity ?: number | null ;
124+ id ?: string | null ;
125+ warehouse_id : string ;
126+ quantity : number ;
120127 minimum_quantity ?: number | null ;
121128 maximum_quantity ?: number | null ;
122- allow_backorder ?: boolean ;
129+ allow_backorder ?: boolean | null ;
130+ active ?: boolean | null ;
131+ }
132+
133+ /**
134+ * Payload vazio para inicializar o sinal `productForm` antes de carregar um produto.
135+ */
136+ export function defaultUpdatePayload ( ) : UpdateProductPayload {
137+ return { } ;
138+ }
139+
140+ /**
141+ * Converte a resposta da API (ProductResponse) no payload de atualização (UpdateProductPayload).
142+ *
143+ * Campos escalares são sempre mapeados.
144+ * Campos relacionais (oem_code_ids, tag_ids, etc.) são mantidos como `undefined`:
145+ * o backend ignora chaves ausentes (regra `array_key_exists`), portanto não haverá
146+ * alteração acidental nos relacionamentos ao salvar sem editar as abas correspondentes.
147+ * Cada aba de edição relacional é responsável por incluir o array correto no payload.
148+ */
149+ export function mapResponseToPayload ( prod : {
150+ category ?: { id : string } | null ;
151+ manufacturer ?: { id : string } | null ;
152+ part_origin ?: { id : string } | null ;
153+ unit ?: { id : string } | null ;
154+ status ?: { id : string } | null ;
155+ internal_code : string ;
156+ barcode ?: string | null ;
157+ name : string ;
158+ slug : string ;
159+ short_description ?: string | null ;
160+ description : string ;
161+ weight : string | number ;
162+ height : string | number ;
163+ width : string | number ;
164+ length : string | number ;
165+ featured : boolean ;
166+ active ?: boolean | null ;
167+ pricing ?: {
168+ regular_price ?: number ;
169+ promotional_price ?: number | null ;
170+ promotion_start ?: string | null ;
171+ promotion_end ?: string | null ;
172+ } | null ;
173+ inventories ?: {
174+ id ?: string ;
175+ warehouse : { id : string } ;
176+ quantity : number ;
177+ minimum_quantity ?: number | null ;
178+ maximum_quantity ?: number | null ;
179+ allow_backorder ?: boolean ;
180+ active ?: boolean ;
181+ } [ ] ;
182+ } ) : UpdateProductPayload {
183+ const firstInventory = prod . inventories ?. [ 0 ] ;
184+
185+ return {
186+ // Referências (IDs de entidades relacionadas)
187+ category_id : prod . category ?. id ,
188+ manufacturer_id : prod . manufacturer ?. id ,
189+ part_origin_id : prod . part_origin ?. id ?? null ,
190+ unit_id : prod . unit ?. id ?? null ,
191+ status_id : prod . status ?. id ,
192+
193+ // Campos escalares
194+ internal_code : prod . internal_code ,
195+ barcode : prod . barcode ?? null ,
196+ name : prod . name ,
197+ slug : prod . slug ,
198+ short_description : prod . short_description ?? null ,
199+ description : prod . description ,
200+
201+ // Dimensões (API retorna como string, payload espera number)
202+ weight : parseFloat ( String ( prod . weight ) ) || 0 ,
203+ height : parseFloat ( String ( prod . height ) ) || 0 ,
204+ width : parseFloat ( String ( prod . width ) ) || 0 ,
205+ length : parseFloat ( String ( prod . length ) ) || 0 ,
206+
207+ featured : prod . featured ,
208+ active : prod . active ?? false ,
209+
210+ // Preço: incluído apenas se existir no produto
211+ price : prod . pricing
212+ ? {
213+ price : prod . pricing . regular_price ,
214+ promotional_price : prod . pricing . promotional_price ?? null ,
215+ promotion_start : prod . pricing . promotion_start ?? null ,
216+ promotion_end : prod . pricing . promotion_end ?? null ,
217+ }
218+ : undefined ,
219+
220+ // Inventário: incluído apenas se o produto já tiver estoque cadastrado
221+ inventories : firstInventory
222+ ? [
223+ {
224+ warehouse_id : firstInventory . warehouse . id ,
225+ quantity : firstInventory . quantity ,
226+ minimum_quantity : firstInventory . minimum_quantity ?? null ,
227+ maximum_quantity : firstInventory . maximum_quantity ?? null ,
228+ allow_backorder : firstInventory . allow_backorder ?? false ,
229+ active : firstInventory . active ?? true ,
230+ } ,
231+ ]
232+ : undefined ,
233+
234+ // Relacionamentos: undefined (não enviados) até o usuário editar a aba correspondente.
235+ // oem_code_ids: undefined,
236+ // product_code_ids: undefined,
237+ // equivalent_product_ids: undefined,
238+ // application_ids: undefined,
239+ // supplier_ids: undefined,
240+ // tag_ids: undefined,
241+ } ;
123242}
0 commit comments