rust_moysklad/models/
variant.rs

1use chrono::NaiveDateTime;
2use serde::{Deserialize, Serialize};
3
4use crate::api_client::MsEntity;
5
6use super::{
7    assortment::{Barcode, BuyPrice, MinPrice, SalePrice},
8    characteristic::Characteristic,
9    deserialize_option_date_from_str,
10    product::{CreateSalePrice, Pack},
11    Meta, MetaWrapper,
12};
13/// Модификация
14///
15/// # Example
16///
17/// ```rust
18/// use anyhow::Result;
19/// use rust_moysklad::{Characteristic, Currency, MoySkladApiClient, Product, Variant};
20/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
21/// #[tokio::main]
22/// async fn main() -> Result<()> {
23///     tracing_subscriber::registry()
24///         .with(
25///             tracing_subscriber::EnvFilter::try_from_default_env()
26///                 .unwrap_or_else(|_| "rust-moysklad=debug".into()),
27///         )
28///         .with(tracing_subscriber::fmt::layer())
29///         .init();
30///     let client = MoySkladApiClient::from_env().expect("MS_TOKEN env var not set!");
31///     let variants = client.get_all::<Variant>().await?;
32///     dbg!(variants.len());
33///     let search_string = "carolus";
34///     let search_result = client.search::<Variant>(search_string).await?;
35///     dbg!(&search_result);
36///     let filtered = client
37///         .filter::<Variant>(
38///             "name",
39///             rust_moysklad::FilterOperator::PartialMatch,
40///             search_string,
41///         )
42///         .await?;
43///     dbg!(&filtered);
44///     let chars = client.get_variants_characteristics().await?;
45///     dbg!(&chars);
46///     let price_types = client.get_price_types().await?;
47///     let products = client.search::<Product>("Краска для разметки").await?;
48///     let currencies = client.get_all::<Currency>().await?;
49///     if let Some(char) = chars.iter().find(|c| c.name == "Ширина рулона, м") {
50///         if let Some(product) = products.first() {
51///             let characteristic = Characteristic::from_variant_char(char.clone(), 4);
52///             let mut variant_to_create = Variant::create(product.meta.clone(), vec![characteristic]);
53///             if let Some(sale_price) = price_types.iter().find(|p| p.name == "Цена продажи")
54///             {
55///                 if let Some(rub) = currencies.iter().find(|c| c.iso_code == "RUB") {
56///                     variant_to_create.sale_price(500000.0, &rub.meta, &sale_price.meta);
57///                 }
58///             }
59///             let vtc = variant_to_create.build();
60///             let created: Variant = client.create(vtc).await?;
61///             dbg!(&created);
62///             let update = Variant::update().description("Test description").build();
63///             let updated: Variant = client.update(created.id, update).await?;
64///             dbg!(&updated);
65///             client.delete::<Variant>(updated.id).await?;
66///         }
67///     }
68///     Ok(())
69/// }
70/// ```
71#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
72#[serde(rename_all = "camelCase")]
73pub struct Variant {
74    pub account_id: uuid::Uuid,
75    pub archived: bool,
76    pub barcodes: Option<Vec<Barcode>>,
77    pub buy_price: Option<BuyPrice>,
78    pub characteristics: Vec<Characteristic>,
79    pub code: Option<String>,
80    pub description: Option<String>,
81    pub discount_prohibited: bool,
82    pub external_code: String,
83    pub id: uuid::Uuid,
84    pub images: Option<MetaWrapper>,
85    pub meta: Meta,
86    pub min_price: Option<MinPrice>,
87    pub name: String,
88    pub packs: Option<Vec<Pack>>,
89    pub product: MetaWrapper,
90    pub sale_prices: Vec<SalePrice>,
91    pub things: Option<Vec<String>>,
92    #[serde(deserialize_with = "deserialize_option_date_from_str")]
93    pub updated: Option<NaiveDateTime>,
94}
95impl Variant {
96    pub fn create(product: Meta, characteristics: Vec<Characteristic>) -> CreateVariantBuilder {
97        CreateVariantBuilder {
98            product: MetaWrapper { meta: product },
99            characteristics,
100            ..Default::default()
101        }
102    }
103    pub fn update() -> UpdateVariantBuilder {
104        UpdateVariantBuilder::default()
105    }
106}
107impl MsEntity for Variant {
108    fn url() -> String {
109        String::from("https://api.moysklad.ru/api/remap/1.2/entity/variant")
110    }
111}
112#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct CreateVariant {
115    #[serde(skip_serializing_if = "Option::is_none")]
116    barcodes: Option<Vec<Barcode>>,
117    #[serde(skip_serializing_if = "Option::is_none")]
118    buy_price: Option<BuyPrice>,
119    characteristics: Vec<Characteristic>,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    code: Option<String>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    description: Option<String>,
124    #[serde(skip_serializing_if = "Option::is_none")]
125    discount_prohibited: Option<bool>,
126    #[serde(skip_serializing_if = "Option::is_none")]
127    external_code: Option<String>,
128    #[serde(skip_serializing_if = "Option::is_none")]
129    images: Option<MetaWrapper>,
130    #[serde(skip_serializing_if = "Option::is_none")]
131    min_price: Option<MinPrice>,
132    name: Option<String>,
133    #[serde(skip_serializing_if = "Option::is_none")]
134    packs: Option<Vec<Pack>>,
135    product: MetaWrapper,
136    sale_prices: Option<Vec<CreateSalePrice>>,
137}
138#[derive(Default)]
139pub struct CreateVariantBuilder {
140    barcodes: Option<Vec<Barcode>>,
141    buy_price: Option<BuyPrice>,
142    characteristics: Vec<Characteristic>,
143    code: Option<String>,
144    description: Option<String>,
145    discount_prohibited: Option<bool>,
146    external_code: Option<String>,
147    images: Option<MetaWrapper>,
148    min_price: Option<MinPrice>,
149    name: Option<String>,
150    packs: Option<Vec<Pack>>,
151    product: MetaWrapper,
152    sale_prices: Option<Vec<CreateSalePrice>>,
153}
154impl CreateVariantBuilder {
155    pub fn barcode<T>(&mut self, barcode: T) -> &mut Self
156    where
157        T: ToString,
158    {
159        self.barcodes.get_or_insert(vec![]).push(Barcode {
160            ean13: barcode.to_string(),
161        });
162        self
163    }
164    pub fn buy_price(&mut self, value: f64, currency: Meta) -> &mut Self {
165        let _ = self.buy_price.insert(BuyPrice {
166            value,
167            currency: MetaWrapper { meta: currency },
168        });
169        self
170    }
171    pub fn characteristic(&mut self, char: Characteristic) -> &mut Self {
172        self.characteristics.push(char);
173        self
174    }
175    pub fn code<T>(&mut self, code: T) -> &mut Self
176    where
177        T: ToString,
178    {
179        let _ = self.code.insert(code.to_string());
180        self
181    }
182    pub fn description<T>(&mut self, description: T) -> &mut Self
183    where
184        T: ToString,
185    {
186        let _ = self.description.insert(description.to_string());
187        self
188    }
189    pub fn discount_prohibited(&mut self, discount_prohibited: bool) -> &mut Self {
190        let _ = self.discount_prohibited.insert(discount_prohibited);
191        self
192    }
193    pub fn external_code<T>(&mut self, external_code: T) -> &mut Self
194    where
195        T: ToString,
196    {
197        let _ = self.external_code.insert(external_code.to_string());
198        self
199    }
200    pub fn min_price(&mut self, value: f64, currency: Meta) -> &mut Self {
201        let _ = self.min_price.insert(MinPrice {
202            value,
203            currency: MetaWrapper { meta: currency },
204        });
205        self
206    }
207    pub fn name<T>(&mut self, name: T) -> &mut Self
208    where
209        T: ToString,
210    {
211        let _ = self.name.insert(name.to_string());
212        self
213    }
214    pub fn sale_price(
215        &mut self,
216        value: f64,
217        currency_meta: &Meta,
218        price_type_meta: &Meta,
219    ) -> &mut Self {
220        self.sale_prices
221            .get_or_insert(vec![])
222            .push(CreateSalePrice {
223                value,
224                currency: MetaWrapper {
225                    meta: currency_meta.to_owned(),
226                },
227                price_type: MetaWrapper {
228                    meta: price_type_meta.to_owned(),
229                },
230            });
231        self
232    }
233    pub fn build(&self) -> CreateVariant {
234        CreateVariant {
235            barcodes: self.barcodes.to_owned(),
236            buy_price: self.buy_price.to_owned(),
237            characteristics: self.characteristics.to_owned(),
238            code: self.code.to_owned(),
239            description: self.description.to_owned(),
240            discount_prohibited: self.discount_prohibited,
241            external_code: self.external_code.to_owned(),
242            images: self.images.to_owned(),
243            min_price: self.min_price.to_owned(),
244            name: self.name.to_owned(),
245            packs: self.packs.to_owned(),
246            product: self.product.to_owned(),
247            sale_prices: self.sale_prices.to_owned(),
248        }
249    }
250}
251#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct UpdateVariant {
254    #[serde(skip_serializing_if = "Option::is_none")]
255    barcodes: Option<Vec<Barcode>>,
256    #[serde(skip_serializing_if = "Option::is_none")]
257    buy_price: Option<BuyPrice>,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    characteristics: Option<Vec<Characteristic>>,
260    #[serde(skip_serializing_if = "Option::is_none")]
261    code: Option<String>,
262    #[serde(skip_serializing_if = "Option::is_none")]
263    description: Option<String>,
264    #[serde(skip_serializing_if = "Option::is_none")]
265    discount_prohibited: Option<bool>,
266    #[serde(skip_serializing_if = "Option::is_none")]
267    external_code: Option<String>,
268    #[serde(skip_serializing_if = "Option::is_none")]
269    id: Option<uuid::Uuid>,
270    #[serde(skip_serializing_if = "Option::is_none")]
271    images: Option<MetaWrapper>,
272    #[serde(skip_serializing_if = "Option::is_none")]
273    meta: Option<Meta>,
274    #[serde(skip_serializing_if = "Option::is_none")]
275    min_price: Option<MinPrice>,
276    #[serde(skip_serializing_if = "Option::is_none")]
277    name: Option<String>,
278    #[serde(skip_serializing_if = "Option::is_none")]
279    packs: Option<Vec<Pack>>,
280    #[serde(skip_serializing_if = "Option::is_none")]
281    product: Option<MetaWrapper>,
282    #[serde(skip_serializing_if = "Option::is_none")]
283    sale_prices: Option<Vec<CreateSalePrice>>,
284}
285#[derive(Default)]
286pub struct UpdateVariantBuilder {
287    barcodes: Option<Vec<Barcode>>,
288    buy_price: Option<BuyPrice>,
289    characteristics: Option<Vec<Characteristic>>,
290    code: Option<String>,
291    description: Option<String>,
292    discount_prohibited: Option<bool>,
293    external_code: Option<String>,
294    id: Option<uuid::Uuid>,
295    images: Option<MetaWrapper>,
296    meta: Option<Meta>,
297    min_price: Option<MinPrice>,
298    name: Option<String>,
299    packs: Option<Vec<Pack>>,
300    product: Option<MetaWrapper>,
301    sale_prices: Option<Vec<CreateSalePrice>>,
302}
303impl UpdateVariantBuilder {
304    pub fn barcode<T>(&mut self, barcode: T) -> &mut Self
305    where
306        T: ToString,
307    {
308        self.barcodes.get_or_insert(vec![]).push(Barcode {
309            ean13: barcode.to_string(),
310        });
311        self
312    }
313    pub fn buy_price(&mut self, value: f64, currency: Meta) -> &mut Self {
314        let _ = self.buy_price.insert(BuyPrice {
315            value,
316            currency: MetaWrapper { meta: currency },
317        });
318        self
319    }
320    pub fn characteristic(&mut self, char: Characteristic) -> &mut Self {
321        self.characteristics.get_or_insert(vec![]).push(char);
322        self
323    }
324    pub fn code<T>(&mut self, code: T) -> &mut Self
325    where
326        T: ToString,
327    {
328        let _ = self.code.insert(code.to_string());
329        self
330    }
331    pub fn description<T>(&mut self, description: T) -> &mut Self
332    where
333        T: ToString,
334    {
335        let _ = self.description.insert(description.to_string());
336        self
337    }
338    pub fn discount_prohibited(&mut self, discount_prohibited: bool) -> &mut Self {
339        let _ = self.discount_prohibited.insert(discount_prohibited);
340        self
341    }
342    pub fn external_code<T>(&mut self, external_code: T) -> &mut Self
343    where
344        T: ToString,
345    {
346        let _ = self.external_code.insert(external_code.to_string());
347        self
348    }
349    pub fn id(&mut self, id: uuid::Uuid) -> &mut Self {
350        let _ = self.id.insert(id);
351        self
352    }
353    pub fn meta(&mut self, meta: Meta) -> &mut Self {
354        let _ = self.meta.insert(meta);
355        self
356    }
357    pub fn min_price(&mut self, value: f64, currency: Meta) -> &mut Self {
358        let _ = self.min_price.insert(MinPrice {
359            value,
360            currency: MetaWrapper { meta: currency },
361        });
362        self
363    }
364    pub fn name<T>(&mut self, name: T) -> &mut Self
365    where
366        T: ToString,
367    {
368        let _ = self.name.insert(name.to_string());
369        self
370    }
371    pub fn product(&mut self, product_meta: Meta) -> &mut Self {
372        let _ = self.product.insert(MetaWrapper { meta: product_meta });
373        self
374    }
375    pub fn sale_price(
376        &mut self,
377        value: f64,
378        currency_meta: &Meta,
379        price_type_meta: &Meta,
380    ) -> &mut Self {
381        self.sale_prices
382            .get_or_insert(vec![])
383            .push(CreateSalePrice {
384                value,
385                currency: MetaWrapper {
386                    meta: currency_meta.to_owned(),
387                },
388                price_type: MetaWrapper {
389                    meta: price_type_meta.to_owned(),
390                },
391            });
392        self
393    }
394    pub fn build(&self) -> UpdateVariant {
395        UpdateVariant {
396            barcodes: self.barcodes.to_owned(),
397            buy_price: self.buy_price.to_owned(),
398            characteristics: self.characteristics.to_owned(),
399            code: self.code.to_owned(),
400            description: self.description.to_owned(),
401            discount_prohibited: self.discount_prohibited,
402            external_code: self.external_code.to_owned(),
403            images: self.images.to_owned(),
404            min_price: self.min_price.to_owned(),
405            name: self.name.to_owned(),
406            packs: self.packs.to_owned(),
407            product: self.product.to_owned(),
408            sale_prices: self.sale_prices.to_owned(),
409            id: self.id,
410            meta: self.meta.to_owned(),
411        }
412    }
413}