samling/prices/
entities.rs

1use chrono::{DateTime, FixedOffset, NaiveDate};
2use rust_decimal::Decimal;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    auth::User,
8    entity_ref::{ExternalId, ExternalIdEntity},
9    helpers::slugify,
10    organizations::Organization,
11    EntityRefPathParam, Error, Id, Ref, RefTarget, Slug, SlugEntity, Style, StyleSummary,
12};
13use samling_clorinde::{
14    client::GenericClient,
15    queries::{price::get_price_id, pricelist::get_pricelist_id},
16};
17
18/// PriceList
19#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
20pub struct PriceList {
21    pub id: Id<Self>,
22    pub slug: Slug<Self>,
23    pub external_id: Option<ExternalId<Self>>,
24    pub name: String,
25    pub created_by: Option<Id<User>>,
26    pub created_at: DateTime<FixedOffset>,
27    pub updated_at: DateTime<FixedOffset>,
28}
29
30/// Price list summary
31#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
32pub struct PriceListSummary {
33    pub id: Id<PriceList>,
34    pub slug: Slug<PriceList>,
35    pub external_id: Option<ExternalId<PriceList>>,
36    pub name: String,
37}
38
39impl EntityRefPathParam for PriceList {
40    fn parameter_name() -> &'static str {
41        "price_list_ref"
42    }
43}
44
45impl RefTarget for PriceList {
46    async fn lookup_id(
47        client: &impl GenericClient,
48        organization_id: Id<Organization>,
49        entity_ref: &crate::entity_ref::Ref<Self>,
50    ) -> crate::Result<Option<Id<Self>>> {
51        let (id, external_id, slug) = entity_ref.to_owned().take_all_inner();
52        Ok(get_pricelist_id()
53            .bind(
54                client,
55                &organization_id.into(),
56                &id,
57                &external_id.as_deref(),
58                &slug,
59            )
60            .opt()
61            .await?
62            .map(|id| id.into()))
63    }
64}
65
66/// PriceList, for creation
67#[derive(Debug, Clone, Deserialize)]
68pub struct CreatePriceList {
69    pub name: String,
70    pub slug: Option<Slug<PriceList>>,
71    pub external_id: Option<ExternalId<PriceList>>,
72}
73
74impl ExternalIdEntity for CreatePriceList {
75    type RefTarget = PriceList;
76
77    fn external_id(&self) -> Option<ExternalId<Self::RefTarget>> {
78        self.external_id.clone()
79    }
80
81    fn set_external_id(&mut self, value: ExternalId<Self::RefTarget>) {
82        self.external_id = Some(value);
83    }
84}
85
86impl SlugEntity for CreatePriceList {
87    type RefTarget = PriceList;
88    fn generate_slug(&self, prefix: &str) -> Option<Slug<Self::RefTarget>> {
89        Some(Slug::new(slugify(&[prefix, &self.name])))
90    }
91
92    fn slug(&self) -> Option<Slug<Self::RefTarget>> {
93        self.slug.clone()
94    }
95
96    fn set_slug(&mut self, value: Slug<Self::RefTarget>) {
97        self.slug = Some(value);
98    }
99}
100
101/// PriceList, for update
102#[derive(Debug, Clone, Deserialize)]
103pub struct UpdatePriceList {
104    pub name: Option<String>,
105    pub slug: Option<Slug<PriceList>>,
106    pub external_id: Option<ExternalId<PriceList>>,
107}
108
109impl From<CreatePriceList> for UpdatePriceList {
110    fn from(pricelist: CreatePriceList) -> Self {
111        UpdatePriceList {
112            name: Some(pricelist.name),
113            slug: pricelist.slug,
114            external_id: pricelist.external_id,
115        }
116    }
117}
118
119impl SlugEntity for UpdatePriceList {
120    type RefTarget = PriceList;
121    fn slug(&self) -> Option<Slug<Self::RefTarget>> {
122        self.slug.clone()
123    }
124
125    fn set_slug(&mut self, value: Slug<Self::RefTarget>) {
126        self.slug = Some(value);
127    }
128}
129
130impl ExternalIdEntity for UpdatePriceList {
131    type RefTarget = PriceList;
132
133    fn external_id(&self) -> Option<ExternalId<Self::RefTarget>> {
134        self.external_id.clone()
135    }
136
137    fn set_external_id(&mut self, value: ExternalId<Self::RefTarget>) {
138        self.external_id = Some(value);
139    }
140}
141
142/// Price set (belonging to the same list and style, but with different start/end dates)
143#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
144pub struct Price {
145    pub id: Id<Self>,
146    pub r#type: PriceType,
147    pub uom: Option<String>,
148    pub currency: String,
149    #[serde(serialize_with = "rust_decimal::serde::float::serialize")]
150    pub amount: Decimal,
151    pub start: NaiveDate,
152    pub end: NaiveDate,
153    pub external_id: Option<ExternalId<Self>>,
154    pub created_by: Option<Id<User>>,
155    pub style: StyleSummary,
156    pub list: PriceListSummary,
157    pub created_at: DateTime<FixedOffset>,
158    pub updated_at: DateTime<FixedOffset>,
159}
160
161/// Nested price set, for inclusion in a NestedStyle
162#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
163pub struct NestedPrice {
164    pub id: Id<Price>,
165    #[serde(serialize_with = "rust_decimal::serde::float::serialize")]
166    pub amount: Decimal,
167    pub start: NaiveDate,
168    pub end: NaiveDate,
169    pub r#type: PriceType,
170    pub uom: Option<String>,
171    pub currency: String,
172    pub list: PriceListSummary,
173}
174impl NestedPrice {
175    pub(crate) fn is_unit_price(&self) -> bool {
176        self.r#type == PriceType::Unit
177    }
178    pub(crate) fn is_retail_price(&self) -> bool {
179        self.r#type == PriceType::Retail
180    }
181}
182
183impl EntityRefPathParam for Price {
184    fn parameter_name() -> &'static str {
185        "price_ref"
186    }
187}
188
189impl RefTarget for Price {
190    async fn lookup_id(
191        client: &impl GenericClient,
192        organization_id: Id<Organization>,
193        entity_ref: &crate::entity_ref::Ref<Self>,
194    ) -> crate::Result<Option<Id<Self>>> {
195        let (id, external_id, slug) = entity_ref.to_owned().take_all_inner();
196        if slug.is_some() {
197            Err(Error::SlugReferenceUnsupported(Self::short_type_name()))
198        } else {
199            Ok(get_price_id()
200                .bind(
201                    client,
202                    &organization_id.into(),
203                    &id,
204                    &external_id.as_deref(),
205                )
206                .opt()
207                .await?
208                .map(|id| id.into()))
209        }
210    }
211}
212
213/// Price, for creation
214#[derive(Debug, Clone, Deserialize)]
215pub struct CreatePrice {
216    pub r#type: PriceType,
217    pub uom: Option<String>,
218    pub currency: String,
219    pub amount: Decimal,
220    pub start: NaiveDate,
221    pub end: NaiveDate,
222    pub list: Ref<PriceList>,
223    pub style: Ref<Style>,
224    pub external_id: Option<ExternalId<Price>>,
225}
226
227impl ExternalIdEntity for CreatePrice {
228    type RefTarget = Price;
229
230    fn external_id(&self) -> Option<ExternalId<Self::RefTarget>> {
231        self.external_id.clone()
232    }
233
234    fn set_external_id(&mut self, value: ExternalId<Self::RefTarget>) {
235        self.external_id = Some(value);
236    }
237}
238
239/// Price set, for update
240#[derive(Debug, Clone, Deserialize)]
241pub struct UpdatePrice {
242    pub r#type: Option<PriceType>,
243    pub uom: Option<String>,
244    pub currency: Option<String>,
245    pub amount: Option<Decimal>,
246    pub start: Option<NaiveDate>,
247    pub end: Option<NaiveDate>,
248    pub style: Option<Ref<Style>>,
249    pub list: Option<Ref<PriceList>>,
250    pub external_id: Option<ExternalId<Price>>,
251}
252
253impl From<CreatePrice> for UpdatePrice {
254    fn from(price: CreatePrice) -> Self {
255        UpdatePrice {
256            r#type: Some(price.r#type),
257            uom: price.uom,
258            currency: Some(price.currency),
259            amount: Some(price.amount),
260            start: Some(price.start),
261            end: Some(price.end),
262            style: Some(price.style),
263            list: Some(price.list),
264            external_id: price.external_id,
265        }
266    }
267}
268
269impl ExternalIdEntity for UpdatePrice {
270    type RefTarget = Price;
271
272    fn external_id(&self) -> Option<ExternalId<Self::RefTarget>> {
273        self.external_id.clone()
274    }
275
276    fn set_external_id(&mut self, value: ExternalId<Self::RefTarget>) {
277        self.external_id = Some(value);
278    }
279}
280
281#[derive(
282    Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
283)]
284pub enum PriceType {
285    Unit,
286    Retail,
287}