1use chrono::NaiveDateTime;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4
5use crate::{
6 BackordersStatus, CatalogVisibility, MetaData, ProductStatus, ProductType, StockStatus,
7 TaxStatus,
8};
9#[skip_serializing_none]
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ProductModify {
12 id: Option<i32>,
13 name: Option<String>,
14 slug: Option<String>,
15 permalink: Option<String>,
16 #[serde(rename = "type")]
17 product_type: Option<ProductType>,
18 status: Option<ProductStatus>,
19 featured: Option<bool>,
20 catalog_visibility: Option<CatalogVisibility>,
21 description: Option<String>,
22 short_description: Option<String>,
23 sku: Option<String>,
24 regular_price: Option<String>,
25 sale_price: Option<String>,
26 date_on_sale_from: Option<NaiveDateTime>,
27 date_on_sale_to: Option<NaiveDateTime>,
28 #[serde(rename = "virtual")]
29 is_virtual: Option<bool>,
30 downloadable: Option<bool>,
31 downloads: Option<Vec<DownloadDTO>>,
32 download_limit: Option<i32>,
33 download_expiry: Option<i32>,
34 external_url: Option<String>,
35 button_text: Option<String>,
36 tax_status: Option<TaxStatus>,
37 tax_class: Option<String>,
38 manage_stock: Option<bool>,
39 stock_quantity: Option<i32>,
40 stock_status: Option<StockStatus>,
41 backorders: Option<BackordersStatus>,
42 sold_individually: Option<bool>,
43 weight: Option<String>,
44 dimensions: Option<DimensionsDTO>,
45 shipping_class: Option<String>,
46 reviews_allowed: Option<bool>,
47 related_ids: Option<Vec<i32>>,
48 upsell_ids: Option<Vec<i32>>,
49 cross_sell_ids: Option<Vec<i32>>,
50 parent_id: Option<i32>,
51 purchase_note: Option<String>,
52 categories: Option<Vec<CategoryDTO>>,
53 tags: Option<Vec<TagDTO>>,
54 images: Option<Vec<ImageDTO>>,
55 attributes: Option<Vec<AttributeDTO>>,
56 default_attributes: Option<Vec<DefaultAttributeDTO>>,
57 grouped_products: Option<Vec<i32>>,
58 menu_order: Option<i32>,
59 meta_data: Option<Vec<MetaData>>,
60}
61impl ProductModify {
62 pub fn builder() -> ProductModifyBuilder {
63 ProductModifyBuilder::default()
64 }
65}
66#[derive(Default)]
67pub struct ProductModifyBuilder {
68 id: Option<i32>,
69 name: Option<String>,
70 slug: Option<String>,
71 permalink: Option<String>,
72 product_type: Option<ProductType>,
73 status: Option<ProductStatus>,
74 featured: Option<bool>,
75 catalog_visibility: Option<CatalogVisibility>,
76 description: Option<String>,
77 short_description: Option<String>,
78 sku: Option<String>,
79 regular_price: Option<String>,
80 sale_price: Option<String>,
81 date_on_sale_from: Option<NaiveDateTime>,
82 date_on_sale_to: Option<NaiveDateTime>,
83 is_virtual: Option<bool>,
84 downloadable: Option<bool>,
85 downloads: Option<Vec<DownloadDTO>>,
86 download_limit: Option<i32>,
87 download_expiry: Option<i32>,
88 external_url: Option<String>,
89 button_text: Option<String>,
90 tax_status: Option<TaxStatus>,
91 tax_class: Option<String>,
92 manage_stock: Option<bool>,
93 stock_quantity: Option<i32>,
94 stock_status: Option<StockStatus>,
95 backorders: Option<BackordersStatus>,
96 sold_individually: Option<bool>,
97 weight: Option<String>,
98 dimensions: Option<DimensionsDTO>,
99 shipping_class: Option<String>,
100 reviews_allowed: Option<bool>,
101 related_ids: Option<Vec<i32>>,
102 upsell_ids: Option<Vec<i32>>,
103 cross_sell_ids: Option<Vec<i32>>,
104 parent_id: Option<i32>,
105 purchase_note: Option<String>,
106 categories: Option<Vec<CategoryDTO>>,
107 tags: Option<Vec<TagDTO>>,
108 images: Option<Vec<ImageDTO>>,
109 attributes: Option<Vec<AttributeDTO>>,
110 default_attributes: Option<Vec<DefaultAttributeDTO>>,
111 grouped_products: Option<Vec<i32>>,
112 menu_order: Option<i32>,
113 meta_data: Option<Vec<MetaData>>,
114}
115impl ProductModifyBuilder {
116 pub fn id(&mut self, id: i32) -> &mut Self {
118 let _ = self.id.insert(id);
119 self
120 }
121 pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
123 let _ = self.name.insert(name.into());
124 self
125 }
126 pub fn slug(&mut self, slug: impl Into<String>) -> &mut Self {
128 let _ = self.slug.insert(slug.into());
129 self
130 }
131 pub fn permalink(&mut self, permalink: impl Into<String>) -> &mut Self {
133 let _ = self.permalink.insert(permalink.into());
134 self
135 }
136 pub fn product_type(&mut self, product_type: ProductType) -> &mut Self {
138 let _ = self.product_type.insert(product_type);
139 self
140 }
141 pub fn status(&mut self, status: ProductStatus) -> &mut Self {
143 let _ = self.status.insert(status);
144 self
145 }
146 pub fn featured(&mut self) -> &mut Self {
148 let _ = self.featured.insert(true);
149 self
150 }
151 pub fn unfeatured(&mut self) -> &mut Self {
152 let _ = self.featured.insert(false);
153 self
154 }
155 pub fn catalog_visibility(&mut self, catalog_visibility: CatalogVisibility) -> &mut Self {
157 let _ = self.catalog_visibility.insert(catalog_visibility);
158 self
159 }
160 pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
162 let _ = self.description.insert(description.into());
163 self
164 }
165 pub fn short_description(&mut self, short_description: impl Into<String>) -> &mut Self {
167 let _ = self.short_description.insert(short_description.into());
168 self
169 }
170 pub fn sku(&mut self, sku: impl Into<String>) -> &mut Self {
172 let _ = self.sku.insert(sku.into());
173 self
174 }
175 pub fn regular_price(&mut self, regular_price: impl Into<String>) -> &mut Self {
177 let _ = self.regular_price.insert(regular_price.into());
178 self
179 }
180 pub fn sale_price(&mut self, sale_price: impl Into<String>) -> &mut Self {
182 let _ = self.sale_price.insert(sale_price.into());
183 self
184 }
185 pub fn date_on_sale_from(&mut self, year: i32, month: u32, day: u32) -> &mut Self {
187 let dt = chrono::NaiveDate::from_ymd_opt(year, month, day)
188 .unwrap()
189 .and_hms_opt(0, 0, 0)
190 .unwrap();
191 let _ = self.date_on_sale_from.insert(dt);
192 self
193 }
194 pub fn date_on_sale_to(&mut self, year: i32, month: u32, day: u32) -> &mut Self {
196 let dt = chrono::NaiveDate::from_ymd_opt(year, month, day)
197 .unwrap()
198 .and_hms_opt(0, 0, 0)
199 .unwrap();
200 let _ = self.date_on_sale_to.insert(dt);
201 self
202 }
203 pub fn set_virtual(&mut self) -> &mut Self {
205 let _ = self.is_virtual.insert(true);
206 self
207 }
208 pub fn downloadable(&mut self) -> &mut Self {
210 let _ = self.downloadable.insert(true);
211 self
212 }
213 pub fn downloads(&mut self, file_src: impl Into<String>) -> &mut Self {
215 let f = DownloadDTO {
216 file: file_src.into(),
217 };
218 self.downloads.get_or_insert(vec![]).push(f);
219 self
220 }
221 pub fn download_limit(&mut self, download_limit: i32) -> &mut Self {
223 let _ = self.download_limit.insert(download_limit);
224 self
225 }
226 pub fn download_expiry(&mut self, days: i32) -> &mut Self {
228 let _ = self.download_expiry.insert(days);
229 self
230 }
231 pub fn external_url(&mut self, url: impl Into<String>) -> &mut Self {
233 let _ = self.external_url.insert(url.into());
234 self
235 }
236 pub fn button_text(&mut self, button_text: impl Into<String>) -> &mut Self {
238 let _ = self.button_text.insert(button_text.into());
239 self
240 }
241 pub fn tax_status(&mut self, tax_status: TaxStatus) -> &mut Self {
243 let _ = self.tax_status.insert(tax_status);
244 self
245 }
246 pub fn tax_class(&mut self, tax_class: impl Into<String>) -> &mut Self {
248 let _ = self.tax_class.insert(tax_class.into());
249 self
250 }
251 pub fn manage_stock(&mut self) -> &mut Self {
253 let _ = self.manage_stock.insert(true);
254 self
255 }
256 pub fn stock_quantity(&mut self, stock_quantity: i32) -> &mut Self {
258 let _ = self.stock_quantity.insert(stock_quantity);
259 self
260 }
261 pub fn stock_status(&mut self, stock_status: StockStatus) -> &mut Self {
263 let _ = self.stock_status.insert(stock_status);
264 self
265 }
266 pub fn backorders(&mut self, backorders: BackordersStatus) -> &mut Self {
268 let _ = self.backorders.insert(backorders);
269 self
270 }
271 pub fn sold_individually(&mut self) -> &mut Self {
273 let _ = self.sold_individually.insert(true);
274 self
275 }
276 pub fn weight(&mut self, weight: impl Into<String>) -> &mut Self {
278 let _ = self.weight.insert(weight.into());
279 self
280 }
281 pub fn dimensions(
283 &mut self,
284 length: impl Into<String>,
285 width: impl Into<String>,
286 height: impl Into<String>,
287 ) -> &mut Self {
288 let d = DimensionsDTO {
289 length: length.into(),
290 width: width.into(),
291 height: height.into(),
292 };
293 let _ = self.dimensions.insert(d);
294 self
295 }
296 pub fn shipping_class(&mut self, slug: impl Into<String>) -> &mut Self {
298 let _ = self.shipping_class.insert(slug.into());
299 self
300 }
301 pub fn reviews_allowed_set_false(&mut self) -> &mut Self {
303 let _ = self.reviews_allowed.insert(false);
304 self
305 }
306 pub fn related_ids(&mut self, id: i32) -> &mut Self {
308 self.related_ids.get_or_insert(vec![]).push(id);
309 self
310 }
311 pub fn upsell_ids(&mut self, id: i32) -> &mut Self {
313 self.upsell_ids.get_or_insert(vec![]).push(id);
314 self
315 }
316 pub fn cross_sell_ids(&mut self, id: i32) -> &mut Self {
318 self.cross_sell_ids.get_or_insert(vec![]).push(id);
319 self
320 }
321 pub fn parent_id(&mut self, parent_id: i32) -> &mut Self {
323 let _ = self.parent_id.insert(parent_id);
324 self
325 }
326 pub fn purchase_note(&mut self, purchase_note: impl Into<String>) -> &mut Self {
328 let _ = self.purchase_note.insert(purchase_note.into());
329 self
330 }
331 pub fn categories(&mut self, category_id: i32) -> &mut Self {
333 self.categories
334 .get_or_insert(vec![])
335 .push(CategoryDTO { id: category_id });
336 self
337 }
338 pub fn tags(&mut self, tag_id: i32) -> &mut Self {
340 self.tags.get_or_insert(vec![]).push(TagDTO { id: tag_id });
341 self
342 }
343 pub fn images(&mut self, img_src: impl Into<String>) -> &mut Self {
345 self.images.get_or_insert(vec![]).push(ImageDTO {
346 src: img_src.into(),
347 });
348 self
349 }
350 pub fn attribute(&mut self, attribute: AttributeDTO) -> &mut Self {
352 self.attributes.get_or_insert(vec![]).push(attribute);
353 self
354 }
355 pub fn default_attribute(
357 &mut self,
358 id: Option<i32>,
359 name: impl Into<String>,
360 option: impl Into<String>,
361 ) -> &mut Self {
362 self.default_attributes
363 .get_or_insert(vec![])
364 .push(DefaultAttributeDTO {
365 id,
366 name: name.into(),
367 option: option.into(),
368 });
369 self
370 }
371 pub fn grouped_product(&mut self, grouped_product_id: i32) -> &mut Self {
373 self.grouped_products
374 .get_or_insert(vec![])
375 .push(grouped_product_id);
376 self
377 }
378 pub fn menu_order(&mut self, menu_order: i32) -> &mut Self {
380 let _ = self.menu_order.insert(menu_order);
381 self
382 }
383 pub fn meta_data(&mut self, key: impl Into<String>, value: impl Serialize) -> &mut Self {
385 self.meta_data.get_or_insert(vec![]).push(MetaData {
386 id: None,
387 key: key.into(),
388 value: serde_json::json!(value),
389 });
390 self
391 }
392 pub fn build(&self) -> ProductModify {
393 ProductModify {
394 id: self.id,
395 name: self.name.clone(),
396 slug: self.slug.clone(),
397 permalink: self.permalink.clone(),
398 product_type: self.product_type.clone(),
399 status: self.status.clone(),
400 featured: self.featured,
401 catalog_visibility: self.catalog_visibility.clone(),
402 description: self.description.clone(),
403 short_description: self.short_description.clone(),
404 sku: self.sku.clone(),
405 regular_price: self.regular_price.clone(),
406 sale_price: self.sale_price.clone(),
407 date_on_sale_from: self.date_on_sale_from,
408 date_on_sale_to: self.date_on_sale_to,
409 is_virtual: self.is_virtual,
410 downloadable: self.downloadable,
411 downloads: self.downloads.clone(),
412 download_limit: self.download_limit,
413 download_expiry: self.download_expiry,
414 external_url: self.external_url.clone(),
415 button_text: self.button_text.clone(),
416 tax_status: self.tax_status.clone(),
417 tax_class: self.tax_class.clone(),
418 manage_stock: self.manage_stock,
419 stock_quantity: self.stock_quantity,
420 stock_status: self.stock_status.clone(),
421 backorders: self.backorders.clone(),
422 sold_individually: self.sold_individually,
423 weight: self.weight.clone(),
424 dimensions: self.dimensions.clone(),
425 shipping_class: self.shipping_class.clone(),
426 reviews_allowed: self.reviews_allowed,
427 related_ids: self.related_ids.clone(),
428 upsell_ids: self.upsell_ids.clone(),
429 cross_sell_ids: self.cross_sell_ids.clone(),
430 parent_id: self.parent_id,
431 purchase_note: self.purchase_note.clone(),
432 categories: self.categories.clone(),
433 tags: self.tags.clone(),
434 images: self.images.clone(),
435 attributes: self.attributes.clone(),
436 default_attributes: self.default_attributes.clone(),
437 grouped_products: self.grouped_products.clone(),
438 menu_order: self.menu_order,
439 meta_data: self.meta_data.clone(),
440 }
441 }
442}
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct DownloadDTO {
445 pub file: String,
448}
449#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct DimensionsDTO {
451 pub length: String,
453 pub width: String,
455 pub height: String,
457}
458#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct CategoryDTO {
460 pub id: i32,
461}
462#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct TagDTO {
464 pub id: i32,
465}
466#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct ImageDTO {
468 pub src: String,
469 }
472#[skip_serializing_none]
473#[derive(Debug, Clone, Serialize, Deserialize, Default)]
474pub struct AttributeDTO {
475 id: Option<i32>,
476 name: String,
477 position: Option<i32>,
478 visible: bool,
479 variation: bool,
480 options: Vec<String>,
481}
482impl AttributeDTO {
483 pub fn builder() -> AttributeDTOBuilder<NoName, NoOptions> {
484 AttributeDTOBuilder::<NoName, NoOptions>::default()
485 }
486}
487#[derive(Default)]
488pub struct WithName(String);
489#[derive(Default)]
490pub struct NoName;
491#[derive(Default)]
492pub struct Options(Vec<String>);
493#[derive(Default)]
494pub struct NoOptions;
495#[derive(Default)]
496pub struct AttributeDTOBuilder<N, O> {
497 id: Option<i32>,
498 name: N,
499 position: Option<i32>,
500 visible: Option<bool>,
501 variation: Option<bool>,
502 options: O,
503}
504impl<N, O> AttributeDTOBuilder<N, O> {
505 pub fn id(mut self, id: i32) -> Self {
507 let _ = self.id.insert(id);
508 self
509 }
510 pub fn name(self, name: impl Into<String>) -> AttributeDTOBuilder<WithName, O> {
512 AttributeDTOBuilder {
513 id: self.id,
514 name: WithName(name.into()),
515 position: self.position,
516 visible: self.visible,
517 variation: self.variation,
518 options: self.options,
519 }
520 }
521 pub fn position(mut self, position: i32) -> Self {
523 let _ = self.position.insert(position);
524 self
525 }
526 pub fn visible(mut self) -> Self {
528 let _ = self.visible.insert(true);
529 self
530 }
531 pub fn variation(mut self) -> Self {
533 let _ = self.variation.insert(true);
534 self
535 }
536
537 pub fn options(self, options: Vec<String>) -> AttributeDTOBuilder<N, Options> {
539 AttributeDTOBuilder {
540 id: self.id,
541 name: self.name,
542 position: self.position,
543 visible: self.visible,
544 variation: self.variation,
545 options: Options(options),
546 }
547 }
548}
549impl<N> AttributeDTOBuilder<N, Options> {
550 pub fn option(mut self, option: impl Into<String>) -> Self {
551 self.options.0.push(option.into());
552 self
553 }
554}
555impl<N> AttributeDTOBuilder<N, NoOptions> {
556 pub fn option(self, option: impl Into<String>) -> AttributeDTOBuilder<N, Options> {
558 AttributeDTOBuilder {
559 id: self.id,
560 name: self.name,
561 position: self.position,
562 visible: self.visible,
563 variation: self.variation,
564 options: Options(vec![option.into()]),
565 }
566 }
567}
568impl AttributeDTOBuilder<WithName, Options> {
569 pub fn build(self) -> AttributeDTO {
570 AttributeDTO {
571 id: self.id,
572 name: self.name.0,
573 position: self.position,
574 visible: self.visible.unwrap_or(true),
575 variation: self.variation.unwrap_or_default(),
576 options: self.options.0,
577 }
578 }
579}
580#[derive(Debug, Clone, Serialize, Deserialize)]
581#[skip_serializing_none]
582pub struct DefaultAttributeDTO {
583 #[serde(skip_serializing_if = "Option::is_none")]
584 pub id: Option<i32>,
585 pub name: String,
586 pub option: String,
587}