diff --git a/.changes/unreleased/Added-20240813-121838.yaml b/.changes/unreleased/Added-20240813-121838.yaml
new file mode 100644
index 00000000..820abd6a
--- /dev/null
+++ b/.changes/unreleased/Added-20240813-121838.yaml
@@ -0,0 +1,4 @@
+kind: Added
+body: Added resources for associate role and business units and extended project setting
+ options
+time: 2024-08-13T12:18:38.233827008+02:00
diff --git a/Taskfile.yml b/Taskfile.yml
index 85a539ee..0758c795 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -53,5 +53,5 @@ tasks:
CTP_CLIENT_SECRET: x
CTP_PROJECT_KEY: unittest
CTP_SCOPES: manage_project:projectkey
- CTP_API_URL: http://localhost:8989
- CTP_AUTH_URL: http://localhost:8989
+ CTP_API_URL: http://localhost:3000
+ CTP_AUTH_URL: http://localhost:3000
diff --git a/commercetools/resource_shipping_zone_rate.go b/commercetools/resource_shipping_zone_rate.go
index a8b8914e..c8ffe324 100644
--- a/commercetools/resource_shipping_zone_rate.go
+++ b/commercetools/resource_shipping_zone_rate.go
@@ -410,7 +410,7 @@ func findShippingZoneRate(shippingMethod *platform.ShippingMethod, shippingZoneI
for _, zoneRate := range shippingMethod.ZoneRates {
if zoneRate.Zone.ID == shippingZoneID {
for _, shippingRate := range zoneRate.ShippingRates {
- if shippingRate.Price.(platform.CentPrecisionMoney).CurrencyCode == currencyCode {
+ if shippingRate.Price.CurrencyCode == currencyCode {
return &shippingRate, nil
}
}
@@ -435,36 +435,22 @@ func setShippingZoneRateState(d *schema.ResourceData, shippingMethod *platform.S
tiers := flattenShippingZoneRateTiers(shippingRate)
_ = d.Set("shipping_rate_price_tier", tiers)
- if typedPrice, ok := shippingRate.Price.(platform.CentPrecisionMoney); ok {
- price := map[string]any{
- "currency_code": typedPrice.CurrencyCode,
- "cent_amount": typedPrice.CentAmount,
- }
- err = d.Set("price", []any{price})
- if err != nil {
- return err
- }
- } else {
- _ = d.Set("price", nil)
- if err != nil {
- return err
- }
+ price := map[string]any{
+ "currency_code": shippingRate.Price.CurrencyCode,
+ "cent_amount": shippingRate.Price.CentAmount,
+ }
+ err = d.Set("price", []any{price})
+ if err != nil {
+ return err
}
- if typedFreeAbove, ok := (shippingRate.FreeAbove).(platform.CentPrecisionMoney); ok {
- freeAbove := map[string]any{
- "currency_code": typedFreeAbove.CurrencyCode,
- "cent_amount": typedFreeAbove.CentAmount,
- }
- err = d.Set("free_above", []any{freeAbove})
- if err != nil {
- return err
- }
- } else {
- _ = d.Set("free_above", nil)
- if err != nil {
- return err
- }
+ freeAbove := map[string]any{
+ "currency_code": shippingRate.FreeAbove.CurrencyCode,
+ "cent_amount": shippingRate.FreeAbove.CentAmount,
+ }
+ err = d.Set("free_above", []any{freeAbove})
+ if err != nil {
+ return err
}
return nil
}
diff --git a/docs/resources/associate_role.md b/docs/resources/associate_role.md
index 28e3694b..b1a83c37 100644
--- a/docs/resources/associate_role.md
+++ b/docs/resources/associate_role.md
@@ -22,12 +22,44 @@ resource "commercetools_associate_role" "regional_manager" {
name = "Regional Manager - Europe"
permissions = [
"AddChildUnits",
- "UpdateBusinessUnitDetails",
"UpdateAssociates",
+ "UpdateBusinessUnitDetails",
+ "UpdateParentUnit",
+ "ViewMyCarts",
+ "ViewOthersCarts",
+ "UpdateMyCarts",
+ "UpdateOthersCarts",
"CreateMyCarts",
+ "CreateOthersCarts",
"DeleteMyCarts",
- "UpdateMyCarts",
- "ViewMyCarts",
+ "DeleteOthersCarts",
+ "ViewMyOrders",
+ "ViewOthersOrders",
+ "UpdateMyOrders",
+ "UpdateOthersOrders",
+ "CreateMyOrdersFromMyCarts",
+ "CreateMyOrdersFromMyQuotes",
+ "CreateOrdersFromOthersCarts",
+ "CreateOrdersFromOthersQuotes",
+ "ViewMyQuotes",
+ "ViewOthersQuotes",
+ "AcceptMyQuotes",
+ "AcceptOthersQuotes",
+ "DeclineMyQuotes",
+ "DeclineOthersQuotes",
+ "RenegotiateMyQuotes",
+ "RenegotiateOthersQuotes",
+ "ReassignMyQuotes",
+ "ReassignOthersQuotes",
+ "ViewMyQuoteRequests",
+ "ViewOthersQuoteRequests",
+ "UpdateMyQuoteRequests",
+ "UpdateOthersQuoteRequests",
+ "CreateMyQuoteRequestsFromMyCarts",
+ "CreateQuoteRequestsFromOthersCarts",
+ "CreateApprovalRules",
+ "UpdateApprovalRules",
+ "UpdateApprovalFlows",
]
}
```
@@ -37,15 +69,15 @@ resource "commercetools_associate_role" "regional_manager" {
### Required
-- `key` (String) User-defined unique identifier of the AssociateRole.
-- `permissions` (List of String) List of Permissions for the AssociateRole.
+- `key` (String) User-defined unique identifier of the associate role.
+- `permissions` (List of String) List of permissions for the associate role. See the [Associate Role API Documentation](https://docs.commercetools.com/api/projects/associate-roles#ctp:api:type:Permission) for more information.
### Optional
-- `buyer_assignable` (Boolean) Whether the AssociateRole can be assigned to an Associate by a buyer. If false, the AssociateRole can only be assigned using the general endpoint.
-- `name` (String) Name of the AssociateRole.
+- `buyer_assignable` (Boolean) Whether the associate role can be assigned to an associate by a buyer. If false, the associate role can only be assigned using the general endpoint. Defaults to true.
+- `name` (String) Name of the associate role.
### Read-Only
-- `id` (String) Unique identifier of the AssociateRole.
-- `version` (Number) Current version of the AssociateRole.
+- `id` (String) Unique identifier of the associate role.
+- `version` (Number) Current version of the associate role.
diff --git a/docs/resources/business_unit_company.md b/docs/resources/business_unit_company.md
new file mode 100644
index 00000000..99a1192f
--- /dev/null
+++ b/docs/resources/business_unit_company.md
@@ -0,0 +1,137 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "commercetools_business_unit_company Resource - terraform-provider-commercetools"
+subcategory: ""
+description: |-
+ Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Company from the generic BusinessUnit.
+ See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units
+---
+
+# commercetools_business_unit_company (Resource)
+
+Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Company from the generic BusinessUnit.
+
+See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units
+
+## Example Usage
+
+```terraform
+resource "commercetools_store" "my-store" {
+ key = "my-store"
+ name = {
+ en-US = "My store"
+ }
+ countries = ["NL", "BE"]
+ languages = ["en-GB"]
+}
+
+resource "commercetools_business_unit_company" "my-company" {
+ key = "my-company"
+ name = "My company"
+ contact_email = "main@my-company.com"
+
+ address {
+ key = "my-company-address-1"
+ country = "NL"
+ state = "Noord-Holland"
+ city = "Amsterdam"
+ street_name = "Keizersgracht"
+ street_number = "3"
+ additional_street_info = "4th floor"
+ postal_code = "1015 CJ"
+ }
+
+ address {
+ key = "my-company-address-2"
+ country = "NL"
+ state = "Utrecht"
+ city = "Utrecht"
+ street_name = "Oudegracht"
+ street_number = "1"
+ postal_code = "3511 AA"
+ additional_street_info = "Main floor"
+ }
+
+ store {
+ key = commercetools_store.my-store.key
+ }
+
+ billing_address_keys = ["my-company-address-1"]
+ shipping_address_keys = ["my-company-address-1", "my-company-address-2"]
+ default_billing_address_key = "my-company-address-1"
+ default_shipping_address_key = "my-company-address-1"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `key` (String) User-defined unique identifier for the Company.
+- `name` (String) The name of the Company.
+
+### Optional
+
+- `address` (Block List) Addresses used by the Business Unit. (see [below for nested schema](#nestedblock--address))
+- `billing_address_keys` (Set of String) Indexes of entries in addresses to set as billing addresses. The billingAddressIds of the [Customer](https://docs.commercetools.com/api/projects/customers) will be replaced by these addresses.
+- `contact_email` (String) The email address of the Company.
+- `default_billing_address_key` (String) Index of the entry in addresses to set as the default billing address.
+- `default_shipping_address_key` (String) Index of the entry in addresses to set as the default shipping address.
+- `shipping_address_keys` (Set of String) Indexes of entries in addresses to set as shipping addresses. The shippingAddressIds of the [Customer](https://docs.commercetools.com/api/projects/customers) will be replaced by these addresses.
+- `status` (String) The status of the Company.
+- `store` (Block List) Sets the Stores the Business Unit is associated with.
+
+If the Business Unit has Stores defined, then all of its Carts, Orders, Quotes, or Quote Requests must belong to one of the Business Unit's Stores.
+
+If the Business Unit has no Stores, then all of its Carts, Orders, Quotes, or Quote Requests must not belong to any Store. (see [below for nested schema](#nestedblock--store))
+
+### Read-Only
+
+- `id` (String) Unique identifier of the Company.
+- `version` (Number) The current version of the Company.
+
+
+### Nested Schema for `address`
+
+Required:
+
+- `country` (String) Name of the country
+- `key` (String) User-defined identifier of the Address that must be unique when multiple addresses are referenced in BusinessUnits, Customers, and itemShippingAddresses (LineItem-specific addresses) of a Cart, Order, QuoteRequest, or Quote.
+
+Optional:
+
+- `additional_address_info` (String) Further information on the Address
+- `additional_street_info` (String) Further information on the street address
+- `apartment` (String) Name or number of the apartment
+- `building` (String) Name or number of the building
+- `city` (String) Name of the city
+- `company` (String) Name of the company
+- `department` (String) Name of the department
+- `email` (String) Email address
+- `external_id` (String) ID for the contact used in an external system
+- `fax` (String) Fax number
+- `first_name` (String) First name of the contact
+- `last_name` (String) Last name of the contact
+- `mobile` (String) Mobile phone number
+- `phone` (String) Phone number
+- `po_box` (String) Post office box number
+- `postal_code` (String) Postal code
+- `region` (String) Name of the region
+- `salutation` (String) Salutation of the contact, for example Ms., Mr.
+- `state` (String) Name of the state
+- `street_name` (String) Name of the street
+- `street_number` (String) Street number
+- `title` (String) Title of the contact, for example Dr., Prof.
+
+Read-Only:
+
+- `id` (String) Unique identifier of the Address
+
+
+
+### Nested Schema for `store`
+
+Optional:
+
+- `key` (String) User-defined unique identifier of the Store
diff --git a/docs/resources/business_unit_division.md b/docs/resources/business_unit_division.md
new file mode 100644
index 00000000..2c2caa27
--- /dev/null
+++ b/docs/resources/business_unit_division.md
@@ -0,0 +1,162 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "commercetools_business_unit_division Resource - terraform-provider-commercetools"
+subcategory: ""
+description: |-
+ Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Division from the generic BusinessUnit.
+ See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units
+---
+
+# commercetools_business_unit_division (Resource)
+
+Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Division from the generic BusinessUnit.
+
+See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units
+
+## Example Usage
+
+```terraform
+resource "commercetools_store" "my-store" {
+ key = "my-store"
+ name = {
+ en-US = "My store"
+ }
+ countries = ["NL", "BE"]
+ languages = ["en-GB"]
+}
+
+resource "commercetools_business_unit_company" "my-company" {
+ key = "my-company"
+ name = "My company"
+ contact_email = "main@my-company.com"
+}
+
+resource "commercetools_business_unit_division" "my-division" {
+ key = "my-division"
+ name = "My division"
+ contact_email = "my-division@my-company.com"
+ store_mode = "Explicit"
+ status = "Active"
+ associate_mode = "Explicit"
+ approval_rule_mode = "Explicit"
+
+ parent_unit {
+ key = commercetools_business_unit_company.my-company.key
+ }
+
+ store {
+ key = commercetools_store.my-store.key
+ }
+
+ address {
+ key = "my-div-address-1"
+ country = "NL"
+ state = "Utrecht"
+ city = "Utrecht"
+ street_name = "Oudegracht"
+ street_number = "1"
+ postal_code = "3511 AA"
+ additional_street_info = "Main floor"
+ }
+
+ address {
+ key = "my-div-address-2"
+ country = "NL"
+ state = "Zuid-Holland"
+ city = "Leiden"
+ street_name = "Breestraat"
+ street_number = "1"
+ postal_code = "2311 CH"
+ }
+ billing_address_keys = ["my-div-address-1"]
+ shipping_address_keys = ["my-div-address-1", "my-div-address-2"]
+ default_billing_address_key = "my-div-address-1"
+ default_shipping_address_key = "my-div-address-1"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `key` (String) User-defined unique identifier for the Division.
+- `name` (String) The name of the Division.
+
+### Optional
+
+- `address` (Block List) Addresses used by the Business Unit. (see [below for nested schema](#nestedblock--address))
+- `approval_rule_mode` (String) Determines whether the Business Unit can inherit Approval Rules from a parent. Defaults to `ExplicitAndFromParent`.
+- `associate_mode` (String) Determines whether the Business Unit can inherit Associates from a parent. Defaults to `ExplicitAndFromParent`.
+- `billing_address_keys` (List of String) List of the billing addresses used by the Division.
+- `contact_email` (String) The email address of the Division.
+- `default_billing_address_key` (String) Key of the default billing Address.
+- `default_shipping_address_key` (String) Key of the default shipping Address.
+- `parent_unit` (Block, Optional) Reference to a parent Business Unit by its key. (see [below for nested schema](#nestedblock--parent_unit))
+- `shipping_address_keys` (List of String) List of the shipping addresses used by the Division.
+- `status` (String) Indicates whether the Business Unit can be edited and used in [Orders](https://docs.commercetools.com/api/projects/orders). Defaults to `Active`.
+- `store` (Block List) Sets the Stores the Business Unit is associated with.
+
+If the Business Unit has Stores defined, then all of its Carts, Orders, Quotes, or Quote Requests must belong to one of the Business Unit's Stores.
+
+If the Business Unit has no Stores, then all of its Carts, Orders, Quotes, or Quote Requests must not belong to any Store. (see [below for nested schema](#nestedblock--store))
+- `store_mode` (String) Defines whether the Stores of the Business Unit are set directly on the Business Unit or are inherited from a parent. Defaults to `FromParent`
+
+### Read-Only
+
+- `id` (String) Unique identifier of the Division.
+- `version` (Number) The current version of the Division.
+
+
+### Nested Schema for `address`
+
+Required:
+
+- `country` (String) Name of the country
+- `key` (String) User-defined identifier of the Address that must be unique when multiple addresses are referenced in BusinessUnits, Customers, and itemShippingAddresses (LineItem-specific addresses) of a Cart, Order, QuoteRequest, or Quote.
+
+Optional:
+
+- `additional_address_info` (String) Further information on the Address
+- `additional_street_info` (String) Further information on the street address
+- `apartment` (String) Name or number of the apartment
+- `building` (String) Name or number of the building
+- `city` (String) Name of the city
+- `company` (String) Name of the company
+- `department` (String) Name of the department
+- `email` (String) Email address
+- `external_id` (String) ID for the contact used in an external system
+- `fax` (String) Fax number
+- `first_name` (String) First name of the contact
+- `last_name` (String) Last name of the contact
+- `mobile` (String) Mobile phone number
+- `phone` (String) Phone number
+- `po_box` (String) Post office box number
+- `postal_code` (String) Postal code
+- `region` (String) Name of the region
+- `salutation` (String) Salutation of the contact, for example Ms., Mr.
+- `state` (String) Name of the state
+- `street_name` (String) Name of the street
+- `street_number` (String) Street number
+- `title` (String) Title of the contact, for example Dr., Prof.
+
+Read-Only:
+
+- `id` (String) Unique identifier of the Address
+
+
+
+### Nested Schema for `parent_unit`
+
+Optional:
+
+- `id` (String) User-defined unique identifier of the Business Unit
+- `key` (String) User-defined unique key of the Business Unit
+
+
+
+### Nested Schema for `store`
+
+Optional:
+
+- `key` (String) User-defined unique identifier of the Store
diff --git a/docs/resources/category.md b/docs/resources/category.md
index 33d1ed6b..b9a51305 100644
--- a/docs/resources/category.md
+++ b/docs/resources/category.md
@@ -107,7 +107,7 @@ Optional:
Read-Only:
-- `id` (String) The ID of this resource.
+- `id` (String)
### Nested Schema for `assets.sources`
diff --git a/docs/resources/channel.md b/docs/resources/channel.md
index 507c529b..c6902ba2 100644
--- a/docs/resources/channel.md
+++ b/docs/resources/channel.md
@@ -84,7 +84,7 @@ Optional:
Read-Only:
-- `id` (String) The ID of this resource.
+- `id` (String)
diff --git a/docs/resources/project_settings.md b/docs/resources/project_settings.md
index 6fe73cc0..1ea72d3e 100644
--- a/docs/resources/project_settings.md
+++ b/docs/resources/project_settings.md
@@ -49,6 +49,7 @@ resource "commercetools_project_settings" "my-project" {
### Optional
+- `business_units` (Block List) Holds configuration specific to [Business Units](https://docs.commercetools.com/api/projects/business-units#ctp:api:type:BusinessUnit). (see [below for nested schema](#nestedblock--business_units))
- `carts` (Block List) [Carts Configuration](https://docs.commercetools.com/api/projects/project#carts-configuration) (see [below for nested schema](#nestedblock--carts))
- `countries` (List of String) A two-digit country code as per [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
- `currencies` (List of String) A three-digit currency code as per [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217)
@@ -68,6 +69,15 @@ resource "commercetools_project_settings" "my-project" {
- `key` (String) The unique key of the project
- `version` (Number)
+
+### Nested Schema for `business_units`
+
+Optional:
+
+- `my_business_unit_associate_role_key_on_creation` (String) Default Associate Role assigned to the Associate creating a Business Unit using the My Business Unit endpoint. Note that this field cannot be unset once assigned!
+- `my_business_unit_status_on_creation` (String) Status of Business Units created using the My Business Unit endpoint.
+
+
### Nested Schema for `carts`
diff --git a/docs/resources/shipping_zone_rate.md b/docs/resources/shipping_zone_rate.md
index 1341b80f..3a9420c5 100644
--- a/docs/resources/shipping_zone_rate.md
+++ b/docs/resources/shipping_zone_rate.md
@@ -159,5 +159,5 @@ Required:
Import is supported using the following syntax:
```shell
-terraform import commercetools_shipping_zone_rate.my-shipping-zone-rate {shipping-method-id}@{shipping-zone-id}@{currency}
+terraform import commercetools_shipping_zone_rate.my-shipping-zone-rate {my-shipping-method-id}@{my-shipping-zone-id}@{currency}
```
diff --git a/examples/resources/commercetools_associate_role/resource.tf b/examples/resources/commercetools_associate_role/resource.tf
index 1fb59345..2cfb43bd 100644
--- a/examples/resources/commercetools_associate_role/resource.tf
+++ b/examples/resources/commercetools_associate_role/resource.tf
@@ -4,11 +4,43 @@ resource "commercetools_associate_role" "regional_manager" {
name = "Regional Manager - Europe"
permissions = [
"AddChildUnits",
- "UpdateBusinessUnitDetails",
"UpdateAssociates",
+ "UpdateBusinessUnitDetails",
+ "UpdateParentUnit",
+ "ViewMyCarts",
+ "ViewOthersCarts",
+ "UpdateMyCarts",
+ "UpdateOthersCarts",
"CreateMyCarts",
+ "CreateOthersCarts",
"DeleteMyCarts",
- "UpdateMyCarts",
- "ViewMyCarts",
+ "DeleteOthersCarts",
+ "ViewMyOrders",
+ "ViewOthersOrders",
+ "UpdateMyOrders",
+ "UpdateOthersOrders",
+ "CreateMyOrdersFromMyCarts",
+ "CreateMyOrdersFromMyQuotes",
+ "CreateOrdersFromOthersCarts",
+ "CreateOrdersFromOthersQuotes",
+ "ViewMyQuotes",
+ "ViewOthersQuotes",
+ "AcceptMyQuotes",
+ "AcceptOthersQuotes",
+ "DeclineMyQuotes",
+ "DeclineOthersQuotes",
+ "RenegotiateMyQuotes",
+ "RenegotiateOthersQuotes",
+ "ReassignMyQuotes",
+ "ReassignOthersQuotes",
+ "ViewMyQuoteRequests",
+ "ViewOthersQuoteRequests",
+ "UpdateMyQuoteRequests",
+ "UpdateOthersQuoteRequests",
+ "CreateMyQuoteRequestsFromMyCarts",
+ "CreateQuoteRequestsFromOthersCarts",
+ "CreateApprovalRules",
+ "UpdateApprovalRules",
+ "UpdateApprovalFlows",
]
}
diff --git a/examples/resources/commercetools_business_unit/resource.tf b/examples/resources/commercetools_business_unit/resource.tf
deleted file mode 100644
index 3f52cf9b..00000000
--- a/examples/resources/commercetools_business_unit/resource.tf
+++ /dev/null
@@ -1,57 +0,0 @@
-resource "commercetools_business_unit_company" "acme_company" {
- key = "acme-company"
- name = "The ACME Company"
- status = "Active"
- contact_email = "acme@example.com"
-
- store {
- key = "acme-usa"
- type_id = "store"
- }
-
- store {
- key = "acme-germany"
- type_id = "store"
- }
-
- address {
- key = "acme-business-unit-address"
- title = "Acme Business Unit Address"
- salutation = "Mr."
- first_name = "John"
- last_name = "Doe"
- street_name = "Main Street"
- street_number = "1"
- additional_street_info = "Additional Street Info"
- postal_code = "12345"
- city = "Berlin"
- region = "Berlin"
- country = "DE"
- company = "Acme"
- department = "IT"
- building = "Building"
- apartment = "Apartment"
- po_box = "P.O. Box"
- phone = "123456789"
- mobile = "987654321"
- }
-
- default_shipping_address_id = "acme-business-unit-address"
- default_billing_address_id = "acme-business-unit-address"
-}
-
-resource "commercetools_business_unit_division" "acme-willie-coyote" {
- key = "acme-willie-coyote"
- name = "Willie Coyote - Traps for Roadrunners"
- status = "Active"
- contact_email = "acme-traps@example.com"
- store_mode = "FromParent"
- associate_mode = "ExplicitAndFromParent"
-
- // Only available for division business units as the Company
- // business unit has no parent unit and must always be the Top Level Unit.
- parent_unit {
- key = commercetools_business_unit_company.acme-company.key
- type_id = "company"
- }
-}
diff --git a/examples/resources/commercetools_business_unit_company/resource.tf b/examples/resources/commercetools_business_unit_company/resource.tf
new file mode 100644
index 00000000..6f13923a
--- /dev/null
+++ b/examples/resources/commercetools_business_unit_company/resource.tf
@@ -0,0 +1,45 @@
+resource "commercetools_store" "my-store" {
+ key = "my-store"
+ name = {
+ en-US = "My store"
+ }
+ countries = ["NL", "BE"]
+ languages = ["en-GB"]
+}
+
+resource "commercetools_business_unit_company" "my-company" {
+ key = "my-company"
+ name = "My company"
+ contact_email = "main@my-company.com"
+
+ address {
+ key = "my-company-address-1"
+ country = "NL"
+ state = "Noord-Holland"
+ city = "Amsterdam"
+ street_name = "Keizersgracht"
+ street_number = "3"
+ additional_street_info = "4th floor"
+ postal_code = "1015 CJ"
+ }
+
+ address {
+ key = "my-company-address-2"
+ country = "NL"
+ state = "Utrecht"
+ city = "Utrecht"
+ street_name = "Oudegracht"
+ street_number = "1"
+ postal_code = "3511 AA"
+ additional_street_info = "Main floor"
+ }
+
+ store {
+ key = commercetools_store.my-store.key
+ }
+
+ billing_address_keys = ["my-company-address-1"]
+ shipping_address_keys = ["my-company-address-1", "my-company-address-2"]
+ default_billing_address_key = "my-company-address-1"
+ default_shipping_address_key = "my-company-address-1"
+}
diff --git a/examples/resources/commercetools_business_unit_division/resource.tf b/examples/resources/commercetools_business_unit_division/resource.tf
new file mode 100644
index 00000000..02923912
--- /dev/null
+++ b/examples/resources/commercetools_business_unit_division/resource.tf
@@ -0,0 +1,57 @@
+resource "commercetools_store" "my-store" {
+ key = "my-store"
+ name = {
+ en-US = "My store"
+ }
+ countries = ["NL", "BE"]
+ languages = ["en-GB"]
+}
+
+resource "commercetools_business_unit_company" "my-company" {
+ key = "my-company"
+ name = "My company"
+ contact_email = "main@my-company.com"
+}
+
+resource "commercetools_business_unit_division" "my-division" {
+ key = "my-division"
+ name = "My division"
+ contact_email = "my-division@my-company.com"
+ store_mode = "Explicit"
+ status = "Active"
+ associate_mode = "Explicit"
+ approval_rule_mode = "Explicit"
+
+ parent_unit {
+ key = commercetools_business_unit_company.my-company.key
+ }
+
+ store {
+ key = commercetools_store.my-store.key
+ }
+
+ address {
+ key = "my-div-address-1"
+ country = "NL"
+ state = "Utrecht"
+ city = "Utrecht"
+ street_name = "Oudegracht"
+ street_number = "1"
+ postal_code = "3511 AA"
+ additional_street_info = "Main floor"
+ }
+
+ address {
+ key = "my-div-address-2"
+ country = "NL"
+ state = "Zuid-Holland"
+ city = "Leiden"
+ street_name = "Breestraat"
+ street_number = "1"
+ postal_code = "2311 CH"
+ }
+ billing_address_keys = ["my-div-address-1"]
+ shipping_address_keys = ["my-div-address-1", "my-div-address-2"]
+ default_billing_address_key = "my-div-address-1"
+ default_shipping_address_key = "my-div-address-1"
+}
diff --git a/go.mod b/go.mod
index a155a915..6305c62c 100644
--- a/go.mod
+++ b/go.mod
@@ -2,6 +2,8 @@ module github.com/labd/terraform-provider-commercetools
go 1.21
+//replace github.com/labd/commercetools-go-sdk v1.5.1 => ../commercetools-go-sdk
+
require (
github.com/elliotchance/orderedmap/v2 v2.2.0
github.com/elliotchance/pie/v2 v2.8.0
@@ -12,9 +14,10 @@ require (
github.com/hashicorp/terraform-plugin-go v0.23.0
github.com/hashicorp/terraform-plugin-mux v0.16.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0
- github.com/labd/commercetools-go-sdk v1.5.1
+ github.com/labd/commercetools-go-sdk v1.6.0
+ github.com/mitchellh/mapstructure v1.5.0
github.com/stretchr/testify v1.9.0
- golang.org/x/oauth2 v0.20.0
+ golang.org/x/oauth2 v0.21.0
golang.org/x/text v0.15.0
)
@@ -62,7 +65,6 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
- github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index faf9a823..f52b74a8 100644
--- a/go.sum
+++ b/go.sum
@@ -143,8 +143,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/labd/commercetools-go-sdk v1.5.1 h1:1JhKrfokvbTU+BqwfZLgMrUb+R5XEAfSscI4Yk8ic68=
-github.com/labd/commercetools-go-sdk v1.5.1/go.mod h1:ta63PQfTBeuwxvUECiJFPZf87E4M0h/vfzeJY54YnsI=
+github.com/labd/commercetools-go-sdk v1.6.0 h1:f+hXCSea6WSsANzPliUZes/qbIBBMyhZF1WhBcnLneM=
+github.com/labd/commercetools-go-sdk v1.6.0/go.mod h1:3K76EpprufmZhqmcEZ+lPAKeK7tDUzEq81gT9pzrvyQ=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@@ -230,8 +230,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
-golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index 7e9b4d1e..f5e93baf 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -20,7 +20,8 @@ import (
datasourcetype "github.com/labd/terraform-provider-commercetools/internal/datasource/type"
"github.com/labd/terraform-provider-commercetools/internal/resources/associate_role"
"github.com/labd/terraform-provider-commercetools/internal/resources/attribute_group"
- "github.com/labd/terraform-provider-commercetools/internal/resources/business_unit"
+ "github.com/labd/terraform-provider-commercetools/internal/resources/business_unit_company"
+ "github.com/labd/terraform-provider-commercetools/internal/resources/business_unit_division"
"github.com/labd/terraform-provider-commercetools/internal/resources/product_selection"
"github.com/labd/terraform-provider-commercetools/internal/resources/project"
"github.com/labd/terraform-provider-commercetools/internal/resources/state"
@@ -197,7 +198,7 @@ func (p *ctProvider) Resources(_ context.Context) []func() resource.Resource {
attribute_group.NewResource,
associate_role.NewResource,
product_selection.NewResource,
- business_unit.NewCompanyResource,
- business_unit.NewDivisionResource,
+ business_unit_company.NewCompanyResource,
+ business_unit_division.NewDivisionResource,
}
}
diff --git a/internal/resources/associate_role/model.go b/internal/resources/associate_role/model.go
index d124b01b..4594953f 100644
--- a/internal/resources/associate_role/model.go
+++ b/internal/resources/associate_role/model.go
@@ -50,7 +50,7 @@ func (ar AssociateRole) updateActions(plan AssociateRole) platform.AssociateRole
}
// setName
- if ar.Name != plan.Name {
+ if !ar.Name.Equal(plan.Name) {
var newName *string
if !plan.Name.IsNull() && !plan.Name.IsUnknown() {
newName = utils.StringRef(plan.Name.ValueString())
@@ -63,7 +63,7 @@ func (ar AssociateRole) updateActions(plan AssociateRole) platform.AssociateRole
}
// setBuyerAssignable value
- if ar.BuyerAssignable != plan.BuyerAssignable {
+ if !ar.BuyerAssignable.Equal(plan.BuyerAssignable) {
result.Actions = append(
result.Actions,
platform.AssociateRoleChangeBuyerAssignableAction{
diff --git a/internal/resources/associate_role/resource.go b/internal/resources/associate_role/resource.go
index 03d7890c..6d7029df 100644
--- a/internal/resources/associate_role/resource.go
+++ b/internal/resources/associate_role/resource.go
@@ -2,6 +2,9 @@ package associate_role
import (
"context"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
+ "regexp"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
@@ -36,37 +39,91 @@ func NewResource() resource.Resource {
// Schema implements resource.Resource.
func (*associateRoleResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
- Description: "Associate Roles provide a way to group granular Permissions and assign " +
+ MarkdownDescription: "Associate Roles provide a way to group granular Permissions and assign " +
"them to Associates within a Business Unit.\n\n" +
"See also the [Associate Role API Documentation](https://docs.commercetools.com/api/projects/associate-roles)",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
- Description: "Unique identifier of the AssociateRole.",
- Computed: true,
+ MarkdownDescription: "Unique identifier of the associate role.",
+ Computed: true,
},
"version": schema.Int64Attribute{
- Description: "Current version of the AssociateRole.",
- Computed: true,
+ MarkdownDescription: "Current version of the associate role.",
+ Computed: true,
},
"key": schema.StringAttribute{
- Description: "User-defined unique identifier of the AssociateRole.",
- Required: true,
+ MarkdownDescription: "User-defined unique identifier of the associate role.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.LengthBetween(2, 256),
+ stringvalidator.RegexMatches(
+ regexp.MustCompile("^[A-Za-z0-9_-]+$"),
+ "Key must match pattern ^[A-Za-z0-9_-]+$",
+ ),
+ },
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Name of the associate role.",
+ Optional: true,
},
"buyer_assignable": schema.BoolAttribute{
- Description: "Whether the AssociateRole can be assigned to an Associate by a buyer. If false, " +
- "the AssociateRole can only be assigned using the general endpoint.",
+ MarkdownDescription: "Whether the associate role can be assigned to an associate by a buyer. If false, " +
+ "the associate role can only be assigned using the general endpoint. Defaults to true.",
Optional: true,
- },
- "name": schema.StringAttribute{
- Description: "Name of the AssociateRole.",
- Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(true),
},
"permissions": schema.ListAttribute{
Required: true,
ElementType: types.StringType,
- Description: "List of Permissions for the AssociateRole.",
+ MarkdownDescription: "List of permissions for the associate role. See the [Associate Role API " +
+ "Documentation](https://docs.commercetools.com/api/projects/associate-roles#ctp:api:type:Permission) " +
+ "for more information.",
Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
+ listvalidator.UniqueValues(),
+ listvalidator.ValueStringsAre(
+ stringvalidator.OneOf(
+ string(platform.PermissionAddChildUnits),
+ string(platform.PermissionUpdateAssociates),
+ string(platform.PermissionUpdateBusinessUnitDetails),
+ string(platform.PermissionUpdateParentUnit),
+ string(platform.PermissionViewMyCarts),
+ string(platform.PermissionViewOthersCarts),
+ string(platform.PermissionUpdateMyCarts),
+ string(platform.PermissionUpdateOthersCarts),
+ string(platform.PermissionCreateMyCarts),
+ string(platform.PermissionCreateOthersCarts),
+ string(platform.PermissionDeleteMyCarts),
+ string(platform.PermissionDeleteOthersCarts),
+ string(platform.PermissionViewMyOrders),
+ string(platform.PermissionViewOthersOrders),
+ string(platform.PermissionUpdateMyOrders),
+ string(platform.PermissionUpdateOthersOrders),
+ string(platform.PermissionCreateMyOrdersFromMyCarts),
+ string(platform.PermissionCreateMyOrdersFromMyQuotes),
+ string(platform.PermissionCreateOrdersFromOthersCarts),
+ string(platform.PermissionCreateOrdersFromOthersQuotes),
+ string(platform.PermissionViewMyQuotes),
+ string(platform.PermissionViewOthersQuotes),
+ string(platform.PermissionAcceptMyQuotes),
+ string(platform.PermissionAcceptOthersQuotes),
+ string(platform.PermissionDeclineMyQuotes),
+ string(platform.PermissionDeclineOthersQuotes),
+ string(platform.PermissionRenegotiateMyQuotes),
+ string(platform.PermissionRenegotiateOthersQuotes),
+ string(platform.PermissionReassignMyQuotes),
+ string(platform.PermissionReassignOthersQuotes),
+ string(platform.PermissionViewMyQuoteRequests),
+ string(platform.PermissionViewOthersQuoteRequests),
+ string(platform.PermissionUpdateMyQuoteRequests),
+ string(platform.PermissionUpdateOthersQuoteRequests),
+ string(platform.PermissionCreateMyQuoteRequestsFromMyCarts),
+ string(platform.PermissionCreateQuoteRequestsFromOthersCarts),
+ string(platform.PermissionCreateApprovalRules),
+ string(platform.PermissionUpdateApprovalRules),
+ string(platform.PermissionUpdateApprovalFlows),
+ ),
+ ),
},
PlanModifiers: []planmodifier.List{
listplanmodifier.UseStateForUnknown(),
diff --git a/internal/resources/business_unit/model.go b/internal/resources/business_unit/model.go
deleted file mode 100644
index d7552d07..00000000
--- a/internal/resources/business_unit/model.go
+++ /dev/null
@@ -1,809 +0,0 @@
-package business_unit
-
-import (
- "reflect"
-
- "github.com/elliotchance/pie/v2"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/labd/commercetools-go-sdk/platform"
- "github.com/labd/terraform-provider-commercetools/internal/utils"
-)
-
-const (
- // Store modes for business units.
- StoreModeExplicit = "Explicit"
- StoreModeFromParent = "FromParent"
-
- // Statuses for business units.
- BusinessUnitActive = "Active"
- BusinessUnitInactive = "Inactive"
-
- // Unit types for business units.
- CompanyType = "Company"
- DivisionType = "Division"
-
- // Associate modes for business units.
- ExplicitAssociateMode = "Explicit"
- ExplicitAndFromParentAssociateMode = "ExplicitAndFromParent"
-
- // Associate role inheritance for business units.
- AssociateRoleInheritanceEnabled = "Enabled"
- AssociateRoleInheritanceDisabled = "Disabled"
-
- // Store type id.
- StoreTypeID = "store"
- AssociateRoleTypeID = "associate-role"
- BusinessUnitTypeID = "business-unit"
- CustomerTypeID = "customer"
-)
-
-/*
-Model types for the business unit resource.
-*/
-type Company struct {
- ID types.String `tfsdk:"id"`
- Version types.Int64 `tfsdk:"version"`
- Key types.String `tfsdk:"key"`
- Name types.String `tfsdk:"name"`
- Status types.String `tfsdk:"status"`
- ContactEmail types.String `tfsdk:"contact_email"`
- Stores []StoreKeyReference `tfsdk:"store"`
- Addresses []Address `tfsdk:"address"`
- ShippingAddressIDs []types.String `tfsdk:"shipping_address_ids"`
- DefaultShippingAddressID types.String `tfsdk:"default_shipping_address_id"`
- BillingAddressIDs []types.String `tfsdk:"billing_address_ids"`
- DefaultBillingAddressID types.String `tfsdk:"default_billing_address_id"`
- Associates []Associate `tfsdk:"associate"`
-}
-
-func (c Company) draft() platform.CompanyDraft {
- status := platform.BusinessUnitStatus(c.Status.ValueString())
- dsa := pie.Int(c.DefaultShippingAddressID.ValueString())
- dba := pie.Int(c.DefaultBillingAddressID.ValueString())
-
- return platform.CompanyDraft{
- Key: c.Key.ValueString(),
- Status: &status,
- Stores: pie.Map(c.Stores, func(s StoreKeyReference) platform.StoreResourceIdentifier {
- return platform.StoreResourceIdentifier{
- Key: s.Key.ValueStringPointer(),
- ID: s.Key.ValueStringPointer(),
- }
- }),
- Name: c.Name.ValueString(),
- ContactEmail: c.ContactEmail.ValueStringPointer(),
- Associates: pie.Map(c.Associates, func(a Associate) platform.AssociateDraft {
- return a.draft()
- }),
- Addresses: pie.Map(c.Addresses, func(a Address) platform.BaseAddress {
- return a.draft()
- }),
- ShippingAddresses: pie.Map(c.ShippingAddressIDs, func(id types.String) int {
- return pie.Int(id.ValueString())
- }),
- DefaultShippingAddress: &dsa,
- BillingAddresses: pie.Map(c.BillingAddressIDs, func(id types.String) int {
- return pie.Int(id.ValueString())
- }),
- DefaultBillingAddress: &dba,
- }
-}
-
-func (c Company) updateActions(plan Company) platform.BusinessUnitUpdate {
- result := platform.BusinessUnitUpdate{
- Version: int(c.Version.ValueInt64()),
- Actions: []platform.BusinessUnitUpdateAction{},
- }
-
- if c.Name.ValueString() != plan.Name.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitChangeNameAction{
- Name: plan.Name.ValueString(),
- })
- }
-
- if c.ContactEmail.ValueString() != plan.ContactEmail.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetContactEmailAction{
- ContactEmail: plan.ContactEmail.ValueStringPointer(),
- })
- }
-
- if c.Status.ValueString() != plan.Status.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitChangeStatusAction{
- Status: plan.Status.ValueString(),
- })
- }
-
- if c.DefaultShippingAddressID.ValueString() != plan.DefaultShippingAddressID.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultShippingAddressAction{
- AddressId: plan.DefaultShippingAddressID.ValueStringPointer(),
- })
- }
-
- if c.DefaultBillingAddressID.ValueString() != plan.DefaultBillingAddressID.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultBillingAddressAction{
- AddressId: plan.DefaultBillingAddressID.ValueStringPointer(),
- })
- }
-
- if !reflect.DeepEqual(c.Stores, plan.Stores) {
- // find stores to be added
- for _, store := range plan.Stores {
- if !pie.Contains(c.Stores, store) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: store.Key.ValueStringPointer(),
- },
- })
- }
- }
-
- // find stores to be removed
- for _, store := range c.Stores {
- if !pie.Contains(plan.Stores, store) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: store.Key.ValueStringPointer(),
- },
- })
- }
- }
- }
-
- if !reflect.DeepEqual(c.Addresses, plan.Addresses) {
- // find addresses to be added
- for _, address := range plan.Addresses {
- if !pie.Contains(c.Addresses, address) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddAddressAction{
- Address: address.draft(),
- })
- }
- }
-
- // find addresses to be removed
- for _, address := range c.Addresses {
- if !pie.Contains(plan.Addresses, address) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveAddressAction{
- AddressId: address.ID.ValueStringPointer(),
- AddressKey: address.Key.ValueStringPointer(),
- })
- }
- }
- }
-
- if !reflect.DeepEqual(c.ShippingAddressIDs, plan.ShippingAddressIDs) {
- // find shipping addresses to be added
- for _, id := range plan.ShippingAddressIDs {
- if !pie.Contains(c.ShippingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddShippingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
-
- // find shipping addresses to be removed
- for _, id := range c.ShippingAddressIDs {
- if !pie.Contains(plan.ShippingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveShippingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
- }
-
- if !reflect.DeepEqual(c.BillingAddressIDs, plan.BillingAddressIDs) {
- // find billing addresses to be added
- for _, id := range plan.BillingAddressIDs {
- if !pie.Contains(c.BillingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddBillingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
-
- // find billing addresses to be removed
- for _, id := range c.BillingAddressIDs {
- if !pie.Contains(plan.BillingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveBillingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
- }
-
- if !reflect.DeepEqual(c.Associates, plan.Associates) {
- result.Actions = append(result.Actions, platform.BusinessUnitSetAssociatesAction{
- Associates: pie.Map(plan.Associates, func(a Associate) platform.AssociateDraft {
- return a.draft()
- }),
- })
- }
-
- return result
-}
-
-// NewCompanyFromNative creates a new Company from a platform.Company.
-func NewCompanyFromNative(cc platform.BusinessUnit) Company {
- c, ok := cc.(Company)
- if !ok {
- return Company{}
- }
-
- company := Company{
- ID: types.StringPointerValue(utils.StringRef(c.ID)),
- Version: types.Int64PointerValue(c.Version.ValueInt64Pointer()),
- Key: types.StringPointerValue(utils.StringRef(c.Key)),
- Name: types.StringPointerValue(utils.StringRef(c.Name)),
- Status: types.StringPointerValue(utils.StringRef(c.Status)),
- ContactEmail: types.StringPointerValue(c.ContactEmail.ValueStringPointer()),
- DefaultShippingAddressID: types.StringPointerValue(c.DefaultShippingAddressID.ValueStringPointer()),
- DefaultBillingAddressID: types.StringPointerValue(c.DefaultBillingAddressID.ValueStringPointer()),
- Stores: make([]StoreKeyReference, len(c.Stores)),
- Addresses: make([]Address, len(c.Addresses)),
- ShippingAddressIDs: make([]types.String, len(c.ShippingAddressIDs)),
- BillingAddressIDs: make([]types.String, len(c.BillingAddressIDs)),
- Associates: make([]Associate, len(c.Associates)),
- }
-
- for i, store := range c.Stores {
- company.Stores[i] = StoreKeyReference{
- Key: types.StringValue(store.Key.ValueString()),
- }
- }
-
- copy(company.Addresses, c.Addresses)
-
- for i, id := range c.ShippingAddressIDs {
- company.ShippingAddressIDs[i] = types.StringValue(id.ValueString())
- }
-
- for i, id := range c.BillingAddressIDs {
- company.BillingAddressIDs[i] = types.StringValue(id.ValueString())
- }
-
- copy(company.Associates, c.Associates)
-
- return company
-}
-
-type Division struct {
- ID types.String `tfsdk:"id"`
- Version types.Int64 `tfsdk:"version"`
- Key types.String `tfsdk:"key"`
- Status types.String `tfsdk:"status"`
- ParentUnit BusinessUnitResourceIdentifier `tfsdk:"parent_unit"`
- Stores []StoreKeyReference `tfsdk:"store"`
- StoreMode types.String `tfsdk:"store_mode"`
- Name types.String `tfsdk:"name"`
- ContactEmail types.String `tfsdk:"contact_email"`
- Addresses []Address `tfsdk:"address"`
- ShippingAddressIDs []types.String `tfsdk:"shipping_address_ids"`
- DefaultShippingAddressID types.String `tfsdk:"default_shipping_address_id"`
- BillingAddressIDs []types.String `tfsdk:"billing_address_ids"`
- DefaultBillingAddressID types.String `tfsdk:"default_billing_address_id"`
- AssociateMode types.String `tfsdk:"associate_mode"`
- Associates []Associate `tfsdk:"associate"`
- InheritedAssociates []InheritedAssociate `tfsdk:"inherited_associates"`
-}
-
-func (d Division) draft() platform.DivisionDraft {
- mode := platform.BusinessUnitStoreMode(d.StoreMode.ValueString())
- associateMode := platform.BusinessUnitAssociateMode(d.AssociateMode.ValueString())
- status := platform.BusinessUnitStatus(d.Status.ValueString())
- dsa := pie.Int(d.DefaultShippingAddressID.ValueString())
- dba := pie.Int(d.DefaultBillingAddressID.ValueString())
-
- return platform.DivisionDraft{
- Key: d.Key.ValueString(),
- Status: &status,
- ParentUnit: platform.BusinessUnitResourceIdentifier{
- ID: d.ParentUnit.ID.ValueStringPointer(),
- Key: d.ParentUnit.Key.ValueStringPointer(),
- },
- Stores: pie.Map(d.Stores, func(s StoreKeyReference) platform.StoreResourceIdentifier {
- return platform.StoreResourceIdentifier{
- Key: s.Key.ValueStringPointer(),
- ID: s.Key.ValueStringPointer(),
- }
- }),
- StoreMode: &mode,
- Name: d.Name.ValueString(),
- ContactEmail: d.ContactEmail.ValueStringPointer(),
- AssociateMode: &associateMode,
- Associates: pie.Map(d.Associates, func(a Associate) platform.AssociateDraft {
- return a.draft()
- }),
- Addresses: pie.Map(d.Addresses, func(a Address) platform.BaseAddress {
- return a.draft()
- }),
- ShippingAddresses: pie.Map(d.ShippingAddressIDs, func(id types.String) int {
- return pie.Int(id.ValueString())
- }),
- DefaultShippingAddress: &dsa,
- BillingAddresses: pie.Map(d.BillingAddressIDs, func(id types.String) int {
- return pie.Int(id.ValueString())
- }),
- DefaultBillingAddress: &dba,
- }
-}
-
-func (d Division) updateActions(plan Division) platform.BusinessUnitUpdate {
- result := platform.BusinessUnitUpdate{
- Version: int(d.Version.ValueInt64()),
- Actions: []platform.BusinessUnitUpdateAction{},
- }
-
- if d.Name.ValueString() != plan.Name.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitChangeNameAction{
- Name: plan.Name.ValueString(),
- })
- }
-
- if d.ContactEmail.ValueString() != plan.ContactEmail.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetContactEmailAction{
- ContactEmail: plan.ContactEmail.ValueStringPointer(),
- })
- }
-
- if d.Status.ValueString() != plan.Status.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitChangeStatusAction{
- Status: plan.Status.ValueString(),
- })
- }
-
- if d.DefaultShippingAddressID.ValueString() != plan.DefaultShippingAddressID.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultShippingAddressAction{
- AddressId: plan.DefaultShippingAddressID.ValueStringPointer(),
- })
- }
-
- if d.DefaultBillingAddressID.ValueString() != plan.DefaultBillingAddressID.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultBillingAddressAction{
- AddressId: plan.DefaultBillingAddressID.ValueStringPointer(),
- })
- }
-
- if d.AssociateMode.ValueString() != plan.AssociateMode.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitChangeAssociateModeAction{
- AssociateMode: platform.BusinessUnitAssociateMode(plan.AssociateMode.ValueString()),
- })
- }
-
- if d.StoreMode.ValueString() != plan.StoreMode.ValueString() {
- result.Actions = append(result.Actions, platform.BusinessUnitSetStoreModeAction{
- StoreMode: platform.BusinessUnitStoreMode(plan.StoreMode.ValueString()),
- })
- }
-
- if !reflect.DeepEqual(d.Stores, plan.Stores) {
- // find stores to be added
- for _, store := range plan.Stores {
- if !pie.Contains(d.Stores, store) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: store.Key.ValueStringPointer(),
- },
- })
- }
- }
-
- // find stores to be removed
- for _, store := range d.Stores {
- if !pie.Contains(plan.Stores, store) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: store.Key.ValueStringPointer(),
- },
- })
- }
- }
-
- if !reflect.DeepEqual(d.BillingAddressIDs, plan.BillingAddressIDs) {
- // find billing addresses to be added
- for _, id := range plan.BillingAddressIDs {
- if !pie.Contains(d.BillingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddBillingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
-
- // find billing addresses to be removed
- for _, id := range d.BillingAddressIDs {
- if !pie.Contains(plan.BillingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveBillingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
- }
- }
-
- if !reflect.DeepEqual(d.Associates, plan.Associates) {
- result.Actions = append(result.Actions, platform.BusinessUnitSetAssociatesAction{
- Associates: pie.Map(plan.Associates, func(a Associate) platform.AssociateDraft {
- return a.draft()
- }),
- })
- }
-
- if !reflect.DeepEqual(d.Addresses, plan.Addresses) {
- // find addresses to be added
- for _, address := range plan.Addresses {
- if !pie.Contains(d.Addresses, address) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddAddressAction{
- Address: address.draft(),
- })
- }
- }
-
- // find addresses to be removed
- for _, address := range d.Addresses {
- if !pie.Contains(plan.Addresses, address) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveAddressAction{
- AddressId: address.ID.ValueStringPointer(),
- AddressKey: address.Key.ValueStringPointer(),
- })
- }
- }
- }
-
- if !reflect.DeepEqual(d.ShippingAddressIDs, plan.ShippingAddressIDs) {
- // find shipping addresses to be added
- for _, id := range plan.ShippingAddressIDs {
- if !pie.Contains(d.ShippingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitAddShippingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
-
- // find shipping addresses to be removed
- for _, id := range d.ShippingAddressIDs {
- if !pie.Contains(plan.ShippingAddressIDs, id) {
- result.Actions = append(result.Actions, platform.BusinessUnitRemoveShippingAddressIdAction{
- AddressId: id.ValueStringPointer(),
- })
- }
- }
- }
-
- return result
-}
-
-// NewDivisionFromNative creates a new Division from a platform.Company.
-func NewDivisionFromNative(cc platform.BusinessUnit) Division {
- c, ok := cc.(Division)
- if !ok {
- return Division{}
- }
-
- parent := BusinessUnitResourceIdentifier{
- ID: types.StringValue(c.ParentUnit.ID.ValueString()),
- Key: types.StringValue(c.ParentUnit.Key.ValueString()),
- }
-
- company := Division{
- ID: types.StringPointerValue(utils.StringRef(c.ID)),
- Version: types.Int64PointerValue(c.Version.ValueInt64Pointer()),
- Key: types.StringPointerValue(utils.StringRef(c.Key)),
- Status: types.StringPointerValue(utils.StringRef(c.Status)),
- ParentUnit: parent,
- StoreMode: types.StringValue(c.StoreMode.ValueString()),
- Name: types.StringPointerValue(utils.StringRef(c.Name)),
- ContactEmail: types.StringPointerValue(c.ContactEmail.ValueStringPointer()),
- DefaultShippingAddressID: types.StringPointerValue(c.DefaultShippingAddressID.ValueStringPointer()),
- DefaultBillingAddressID: types.StringPointerValue(c.DefaultBillingAddressID.ValueStringPointer()),
- AssociateMode: types.StringPointerValue(utils.StringRef(c.AssociateMode)),
- Stores: make([]StoreKeyReference, len(c.Stores)),
- BillingAddressIDs: make([]types.String, len(c.BillingAddressIDs)),
- Addresses: make([]Address, len(c.Addresses)),
- ShippingAddressIDs: make([]types.String, len(c.ShippingAddressIDs)),
- Associates: make([]Associate, len(c.Associates)),
- InheritedAssociates: make([]InheritedAssociate, len(c.InheritedAssociates)),
- }
-
- for i, store := range c.Stores {
- company.Stores[i] = StoreKeyReference{
- Key: types.StringValue(store.Key.ValueString()),
- }
- }
-
- copy(company.Addresses, c.Addresses)
-
- for i, id := range c.ShippingAddressIDs {
- company.ShippingAddressIDs[i] = types.StringValue(id.ValueString())
- }
-
- for i, id := range c.BillingAddressIDs {
- company.BillingAddressIDs[i] = types.StringValue(id.ValueString())
- }
-
- copy(company.Associates, c.Associates)
-
- copy(company.InheritedAssociates, c.InheritedAssociates)
-
- return company
-}
-
-/*
- Support types for the business unit resource.
-*/
-
-type Address struct {
- ID types.String `tfsdk:"id"`
- Key types.String `tfsdk:"key"`
- ExternalID types.String `tfsdk:"external_id"`
- Country types.String `tfsdk:"country"`
- Title types.String `tfsdk:"title"`
- Salutation types.String `tfsdk:"salutation"`
- FirstName types.String `tfsdk:"first_name"`
- LastName types.String `tfsdk:"last_name"`
- StreetName types.String `tfsdk:"street_name"`
- StreetNumber types.String `tfsdk:"street_number"`
- AdditionalStreetInfo types.String `tfsdk:"additional_street_info"`
- PostalCode types.String `tfsdk:"postal_code"`
- City types.String `tfsdk:"city"`
- Region types.String `tfsdk:"region"`
- State types.String `tfsdk:"state"`
- Company types.String `tfsdk:"company"`
- Department types.String `tfsdk:"department"`
- Building types.String `tfsdk:"building"`
- Apartment types.String `tfsdk:"apartment"`
- POBox types.String `tfsdk:"po_box"`
- Phone types.String `tfsdk:"phone"`
- Mobile types.String `tfsdk:"mobile"`
- Email types.String `tfsdk:"email"`
- Fax types.String `tfsdk:"fax"`
- AdditionalAddressInfo types.String `tfsdk:"additional_address_info"`
-}
-
-func (a Address) draft() platform.BaseAddress {
- return platform.BaseAddress{
- Key: a.Key.ValueStringPointer(),
- ExternalId: a.ExternalID.ValueStringPointer(),
- Country: a.Country.ValueString(),
- Title: a.Title.ValueStringPointer(),
- Salutation: a.Salutation.ValueStringPointer(),
- FirstName: a.FirstName.ValueStringPointer(),
- LastName: a.LastName.ValueStringPointer(),
- StreetName: a.StreetName.ValueStringPointer(),
- StreetNumber: a.StreetNumber.ValueStringPointer(),
- AdditionalStreetInfo: a.AdditionalStreetInfo.ValueStringPointer(),
- PostalCode: a.PostalCode.ValueStringPointer(),
- City: a.City.ValueStringPointer(),
- Region: a.Region.ValueStringPointer(),
- State: a.State.ValueStringPointer(),
- Company: a.Company.ValueStringPointer(),
- Department: a.Department.ValueStringPointer(),
- Building: a.Building.ValueStringPointer(),
- Apartment: a.Apartment.ValueStringPointer(),
- POBox: a.POBox.ValueStringPointer(),
- Phone: a.Phone.ValueStringPointer(),
- Mobile: a.Mobile.ValueStringPointer(),
- Email: a.Email.ValueStringPointer(),
- Fax: a.Fax.ValueStringPointer(),
- AdditionalAddressInfo: a.AdditionalAddressInfo.ValueStringPointer(),
- }
-}
-
-// NewAddressFromNative creates a new Address from a platform.Address.
-func NewAddressFromNative(a *platform.Address) Address {
- return Address{
- ID: types.StringPointerValue(a.ID),
- Key: types.StringPointerValue(a.Key),
- ExternalID: types.StringPointerValue(a.ExternalId),
- Country: types.StringValue(a.Country),
- Title: types.StringPointerValue(a.Title),
- Salutation: types.StringPointerValue(a.Salutation),
- FirstName: types.StringPointerValue(a.FirstName),
- LastName: types.StringPointerValue(a.LastName),
- StreetName: types.StringPointerValue(a.StreetName),
- StreetNumber: types.StringPointerValue(a.StreetNumber),
- AdditionalStreetInfo: types.StringPointerValue(a.AdditionalStreetInfo),
- PostalCode: types.StringPointerValue(a.PostalCode),
- City: types.StringPointerValue(a.City),
- Region: types.StringPointerValue(a.Region),
- State: types.StringPointerValue(a.State),
- Company: types.StringPointerValue(a.Company),
- Department: types.StringPointerValue(a.Department),
- Building: types.StringPointerValue(a.Building),
- Apartment: types.StringPointerValue(a.Apartment),
- POBox: types.StringPointerValue(a.POBox),
- Phone: types.StringPointerValue(a.Phone),
- Mobile: types.StringPointerValue(a.Mobile),
- Email: types.StringPointerValue(a.Email),
- Fax: types.StringPointerValue(a.Fax),
- AdditionalAddressInfo: types.StringPointerValue(a.AdditionalAddressInfo),
- }
-}
-
-// Associate is a type to model the fields that all types of Associates have in common.
-type Associate struct {
- AssociateRoleAssignments []AssociateRoleAssignment `tfsdk:"associate_role_assignments"`
- Customer CustomerReference `tfsdk:"customer"`
-}
-
-func (a Associate) draft() platform.AssociateDraft {
- return platform.AssociateDraft{
- AssociateRoleAssignments: pie.Map(a.AssociateRoleAssignments, func(ara AssociateRoleAssignment) platform.AssociateRoleAssignmentDraft {
- return ara.draft()
- }),
- Customer: a.Customer.draft(),
- }
-}
-
-// NewAssociateFromNative creates a new Associate from a platform.Associate.
-func NewAssociateFromNative(a *platform.Associate) Associate {
- assoc := Associate{
- AssociateRoleAssignments: make([]AssociateRoleAssignment, len(a.AssociateRoleAssignments)),
- Customer: NewCustomerReferenceFromNative(&a.Customer),
- }
-
- for i, ara := range a.AssociateRoleAssignments {
- assoc.AssociateRoleAssignments[i] = NewAssociateRoleAssignment(&ara)
- }
-
- return assoc
-}
-
-// AssociateRoleAssignment is a type to model the fields that all types of
-// Associate Role Assignments have in common.
-type AssociateRoleAssignment struct {
- AssociateRole AssociateRoleKeyReference `tfsdk:"associate_role"`
- Inheritance types.String `tfsdk:"inheritance"`
-}
-
-func (ara AssociateRoleAssignment) draft() platform.AssociateRoleAssignmentDraft {
- if ara.Inheritance.IsNull() || ara.Inheritance.IsUnknown() {
- return platform.AssociateRoleAssignmentDraft{
- AssociateRole: ara.AssociateRole.draft(),
- }
- }
-
- inheritance := platform.AssociateRoleInheritanceMode(ara.Inheritance.ValueString())
-
- return platform.AssociateRoleAssignmentDraft{
- AssociateRole: ara.AssociateRole.draft(),
- Inheritance: &inheritance,
- }
-}
-
-// NewAssociateRoleAssignment creates a new AssociateRoleAssignment from a
-// platform.AssociateRoleAssignment.
-func NewAssociateRoleAssignment(ara *platform.AssociateRoleAssignment) AssociateRoleAssignment {
- ar := AssociateRoleAssignment{}
-
- ar.AssociateRole = NewAssociateRoleKeyReferenceFromNative(&ara.AssociateRole)
- ar.Inheritance = types.StringValue(string(ara.Inheritance))
-
- return ar
-}
-
-// AssociateRoleKeyReference is a type to model the fields that all types of
-// Associate Role Key References have in common.
-type AssociateRoleKeyReference struct {
- Key types.String `tfsdk:"key"`
- TypeID types.String `tfsdk:"type_id"`
-}
-
-func (kr AssociateRoleKeyReference) draft() platform.AssociateRoleResourceIdentifier {
- if !kr.Key.IsNull() || !kr.Key.IsUnknown() {
- return platform.AssociateRoleResourceIdentifier{
- Key: kr.Key.ValueStringPointer(),
- }
- }
-
- return platform.AssociateRoleResourceIdentifier{}
-}
-
-// NewAssociateRoleKeyReferenceFromNative creates a new AssociateRoleKeyReference
-// from a platform.AssociateRoleKeyReference.
-func NewAssociateRoleKeyReferenceFromNative(kr *platform.AssociateRoleKeyReference) AssociateRoleKeyReference {
- return AssociateRoleKeyReference{
- Key: types.StringValue(kr.Key),
- TypeID: types.StringValue(AssociateRoleTypeID),
- }
-}
-
-// BusinessUnitKeyReference is a type to model the fields that all types of
-// Business Unit Key References have in common.
-type BusinessUnitKeyReference struct {
- Key types.String `tfsdk:"key"`
- TypeID types.String `tfsdk:"type_id"`
-}
-
-// NewBusinessUnitKeyReferenceFromNative creates a new BusinessUnitKeyReference
-// from a platform.BusinessUnitKeyReference.
-func NewBusinessUnitKeyReferenceFromNative(kr *platform.BusinessUnitKeyReference) BusinessUnitKeyReference {
- return BusinessUnitKeyReference{
- Key: types.StringValue(kr.Key),
- TypeID: types.StringValue(BusinessUnitTypeID),
- }
-}
-
-// BusinessUnitResourceIdentifier is a resource identifier for a business unit.
-type BusinessUnitResourceIdentifier struct {
- ID types.String `tfsdk:"id"`
- Key types.String `tfsdk:"key"`
-}
-
-// NewBusinessUnitResourceIdentifierFromNative creates a new BusinessUnitResourceIdentifier
-// from a platform.BusinessUnitResourceIdentifier.
-func NewBusinessUnitResourceIdentifierFromNative(kr *platform.BusinessUnitResourceIdentifier) BusinessUnitResourceIdentifier {
- return BusinessUnitResourceIdentifier{
- ID: types.StringValue(*kr.ID),
- Key: types.StringValue(*kr.Key),
- }
-}
-
-// CustomerReference is a type to model the fields that all types of
-// Customer References have in common.
-type CustomerReference struct {
- ID types.String `tfsdk:"id"`
- TypeID types.String `tfsdk:"type_id"`
-}
-
-func (cr CustomerReference) draft() platform.CustomerResourceIdentifier {
- if !cr.ID.IsNull() || !cr.ID.IsUnknown() {
- return platform.CustomerResourceIdentifier{
- ID: cr.ID.ValueStringPointer(),
- }
- }
-
- return platform.CustomerResourceIdentifier{}
-}
-
-// NewCustomerReferenceFromNative creates a new CustomerReference from a
-// platform.CustomerReference.
-func NewCustomerReferenceFromNative(kr *platform.CustomerReference) CustomerReference {
- return CustomerReference{
- ID: types.StringValue(kr.ID),
- TypeID: types.StringValue(CustomerTypeID),
- }
-}
-
-// InheritedAssociate is a type to model the fields that all types of Inherited Associates have in common.
-type InheritedAssociate struct {
- AssociateRoleAssignments []InheritedAssociateRoleAssignment `tfsdk:"associate_role_assignment"`
- Customer CustomerReference `tfsdk:"customer"`
-}
-
-// NewInheritedAssociateFromNative creates a new InheritedAssociate from a
-// platform.InheritedAssociate.
-func NewInheritedAssociateFromNative(ia *platform.InheritedAssociate) InheritedAssociate {
- localIA := InheritedAssociate{
- AssociateRoleAssignments: make([]InheritedAssociateRoleAssignment, len(ia.AssociateRoleAssignments)),
- Customer: NewCustomerReferenceFromNative(&ia.Customer),
- }
-
- for i, ara := range ia.AssociateRoleAssignments {
- localIA.AssociateRoleAssignments[i] = NewInheritedAssociateRoleAssignmentFromNative(&ara)
- }
-
- return localIA
-}
-
-// InheritedAssociateRoleAssignment is a type to model the fields that all types of
-// Inherited Associate Role Assignments have in common.
-type InheritedAssociateRoleAssignment struct {
- AssociateRole AssociateRoleKeyReference `tfsdk:"associate_role"`
- Source BusinessUnitKeyReference `tfsdk:"source"`
-}
-
-// NewInheritedAssociateRoleAssignmentFromNative creates a new
-// InheritedAssociateRoleAssignment from a platform.InheritedAssociateRoleAssignment.
-func NewInheritedAssociateRoleAssignmentFromNative(ara *platform.InheritedAssociateRoleAssignment) InheritedAssociateRoleAssignment {
- localARA := InheritedAssociateRoleAssignment{}
-
- localARA.AssociateRole = NewAssociateRoleKeyReferenceFromNative(&ara.AssociateRole)
- localARA.Source = NewBusinessUnitKeyReferenceFromNative(&ara.Source)
-
- return localARA
-}
-
-// StoreKeyReference is a type to model the fields that all types of
-// Store Key References have in common.
-type StoreKeyReference struct {
- Key types.String `tfsdk:"key"`
- TypeID types.String `tfsdk:"type_id"`
-}
diff --git a/internal/resources/business_unit/model_test.go b/internal/resources/business_unit/model_test.go
deleted file mode 100644
index e6d0ac1f..00000000
--- a/internal/resources/business_unit/model_test.go
+++ /dev/null
@@ -1,648 +0,0 @@
-package business_unit
-
-import (
- "testing"
-
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/labd/commercetools-go-sdk/platform"
- "github.com/labd/terraform-provider-commercetools/internal/utils"
- "github.com/stretchr/testify/assert"
-)
-
-func TestBusinessUnit_Company_UpdateActions(t *testing.T) {
- cases := []struct {
- name string
- state Company
- plan Company
- expected platform.BusinessUnitUpdate
- }{
- {
- "business unit update name",
- Company{
- Name: types.StringValue("Example business unit"),
- },
- Company{
- Name: types.StringValue("Updated business unit"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitChangeNameAction{
- Name: "Updated business unit",
- },
- },
- },
- },
- {
- "business unit update contact email",
- Company{
- ContactEmail: types.StringValue("info@example.com"),
- },
- Company{
- ContactEmail: types.StringValue("new@example.com"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetContactEmailAction{
- ContactEmail: types.StringValue("new@example.com").ValueStringPointer(),
- },
- },
- },
- },
- {
- "business unit update status",
- Company{
- Status: types.StringValue("Active"),
- },
- Company{
- Status: types.StringValue("Inactive"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitChangeStatusAction{
- Status: "Inactive",
- },
- },
- },
- },
- {
- "business unit update default shipping address",
- Company{
- DefaultShippingAddressID: types.StringValue("some-random-id"),
- },
- Company{
- DefaultShippingAddressID: types.StringValue("another-random-id"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetDefaultShippingAddressAction{
- AddressId: types.StringValue("another-random-id").ValueStringPointer(),
- },
- },
- },
- },
- {
- "business unit update default billing address",
- Company{
- DefaultBillingAddressID: types.StringValue("some-random-id"),
- },
- Company{
- DefaultBillingAddressID: types.StringValue("another-random-id"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetDefaultBillingAddressAction{
- AddressId: types.StringValue("another-random-id").ValueStringPointer(),
- },
- },
- },
- },
- {
- "business unit update stores",
- Company{
- Stores: []StoreKeyReference{
- {
- Key: types.StringValue("store-1"),
- },
- {
- Key: types.StringValue("store-2"),
- },
- },
- },
- Company{
- Stores: []StoreKeyReference{
- {
- Key: types.StringValue("store-1"),
- },
- {
- Key: types.StringValue("store-3"),
- },
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitAddStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: types.StringValue("store-3").ValueStringPointer(),
- ID: nil,
- },
- },
- platform.BusinessUnitRemoveStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: types.StringValue("store-2").ValueStringPointer(),
- ID: nil,
- },
- },
- },
- },
- },
- {
- "business unit add address",
- Company{
- Addresses: []Address{},
- },
- Company{
- Addresses: []Address{
- {
- Key: types.StringValue("new-york-office"),
- Country: types.StringValue("US"),
- Salutation: types.StringValue("Mr."),
- FirstName: types.StringValue("John"),
- LastName: types.StringValue("Doe"),
- StreetName: types.StringValue("Main St."),
- StreetNumber: types.StringValue("123"),
- AdditionalStreetInfo: types.StringValue("Apt. 1"),
- PostalCode: types.StringValue("12345"),
- City: types.StringValue("New York"),
- Region: types.StringValue("New York"),
- State: types.StringValue("New York"),
- Company: types.StringValue("Example Inc."),
- Department: types.StringValue("Sales"),
- Building: types.StringValue("1"),
- Apartment: types.StringValue("1"),
- POBox: types.StringValue("123"),
- Phone: types.StringValue("1234567890"),
- Mobile: types.StringValue("1234567890"),
- Fax: types.StringValue("1234567890"),
- },
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitAddAddressAction{
- Address: platform.BaseAddress{
- Key: utils.StringRef("new-york-office"),
- Country: "US",
- Salutation: utils.StringRef("Mr."),
- FirstName: utils.StringRef("John"),
- LastName: utils.StringRef("Doe"),
- StreetName: utils.StringRef("Main St."),
- StreetNumber: utils.StringRef("123"),
- AdditionalStreetInfo: utils.StringRef("Apt. 1"),
- PostalCode: utils.StringRef("12345"),
- City: utils.StringRef("New York"),
- Region: utils.StringRef("New York"),
- State: utils.StringRef("New York"),
- Company: utils.StringRef("Example Inc."),
- Department: utils.StringRef("Sales"),
- Building: utils.StringRef("1"),
- Apartment: utils.StringRef("1"),
- POBox: utils.StringRef("123"),
- Phone: utils.StringRef("1234567890"),
- Mobile: utils.StringRef("1234567890"),
- Fax: utils.StringRef("1234567890"),
- },
- },
- },
- },
- },
- {
- "business unit remove address",
- Company{
- Addresses: []Address{
- {
- Key: types.StringValue("new-york-office"),
- Country: types.StringValue("US"),
- Salutation: types.StringValue("Mr."),
- FirstName: types.StringValue("John"),
- LastName: types.StringValue("Doe"),
- StreetName: types.StringValue("Main St."),
- StreetNumber: types.StringValue("123"),
- AdditionalStreetInfo: types.StringValue("Apt. 1"),
- PostalCode: types.StringValue("12345"),
- City: types.StringValue("New York"),
- Region: types.StringValue("New York"),
- State: types.StringValue("New York"),
- Company: types.StringValue("Example Inc."),
- Department: types.StringValue("Sales"),
- Building: types.StringValue("1"),
- Apartment: types.StringValue("1"),
- POBox: types.StringValue("123"),
- Phone: types.StringValue("1234567890"),
- Mobile: types.StringValue("1234567890"),
- Fax: types.StringValue("1234567890"),
- },
- },
- },
- Company{
- Addresses: []Address{},
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitRemoveAddressAction{
- AddressKey: utils.StringRef("new-york-office"),
- },
- },
- },
- },
- {
- "business unit set associates",
- Company{},
- Company{
- Associates: []Associate{
- {
- AssociateRoleAssignments: []AssociateRoleAssignment{
- {
- AssociateRole: AssociateRoleKeyReference{
- Key: types.StringValue("role-1"),
- },
- },
- },
- Customer: CustomerReference{
- ID: types.StringValue("customer-1"),
- },
- },
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetAssociatesAction{
- Associates: []platform.AssociateDraft{
- {
- AssociateRoleAssignments: []platform.AssociateRoleAssignmentDraft{
- {
- AssociateRole: platform.AssociateRoleResourceIdentifier{
- Key: utils.StringRef("role-1"),
- },
- },
- },
- Customer: platform.CustomerResourceIdentifier{
- ID: utils.StringRef("customer-1"),
- },
- },
- },
- },
- },
- },
- },
- {
- "business unit add billing address id",
- Company{
- BillingAddressIDs: []types.String{},
- },
- Company{
- BillingAddressIDs: []types.String{
- types.StringValue("new-york-office"),
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitAddBillingAddressIdAction{
- AddressId: utils.StringRef("new-york-office"),
- },
- },
- },
- },
- {
- "business unit remove billing address id",
- Company{
- BillingAddressIDs: []types.String{
- types.StringValue("new-york-office"),
- },
- },
- Company{
- BillingAddressIDs: []types.String{},
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitRemoveBillingAddressIdAction{
- AddressId: utils.StringRef("new-york-office"),
- },
- },
- },
- },
- {
- "business unit add shipping address id",
- Company{
- ShippingAddressIDs: []types.String{},
- },
- Company{
- ShippingAddressIDs: []types.String{
- types.StringValue("new-york-office"),
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitAddShippingAddressIdAction{
- AddressId: utils.StringRef("new-york-office"),
- },
- },
- },
- },
- {
- "business unit remove shipping address id",
- Company{
- ShippingAddressIDs: []types.String{
- types.StringValue("new-york-office"),
- },
- },
- Company{
- ShippingAddressIDs: []types.String{},
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitRemoveShippingAddressIdAction{
- AddressId: utils.StringRef("new-york-office"),
- },
- },
- },
- },
- }
-
- for _, c := range cases {
- t.Run(c.name, func(t *testing.T) {
- result := c.state.updateActions(c.plan)
- assert.EqualValues(t, c.expected, result)
- })
- }
-}
-
-func TestBusinessUnit_Division_UpdateActions(t *testing.T) {
- cases := []struct {
- name string
- state Division
- plan Division
- expected platform.BusinessUnitUpdate
- }{
- {
- "business unit update name",
- Division{
- Name: types.StringValue("Example business unit"),
- },
- Division{
- Name: types.StringValue("Updated business unit"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitChangeNameAction{
- Name: "Updated business unit",
- },
- },
- },
- },
- {
- "business unit update contact email",
- Division{
- ContactEmail: types.StringValue("info@example.com"),
- },
- Division{
- ContactEmail: types.StringValue("new@example.com"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetContactEmailAction{
- ContactEmail: types.StringValue("new@example.com").ValueStringPointer(),
- },
- },
- },
- },
- {
- "business unit update status",
- Division{
- Status: types.StringValue("Active"),
- },
- Division{
- Status: types.StringValue("Inactive"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitChangeStatusAction{
- Status: "Inactive",
- },
- },
- },
- },
- {
- "business unit update default shipping address",
- Division{
- DefaultShippingAddressID: types.StringValue("some-random-id"),
- },
- Division{
- DefaultShippingAddressID: types.StringValue("another-random-id"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetDefaultShippingAddressAction{
- AddressId: types.StringValue("another-random-id").ValueStringPointer(),
- },
- },
- },
- },
- {
- "business unit update default billing address",
- Division{
- DefaultBillingAddressID: types.StringValue("some-random-id"),
- },
- Division{
- DefaultBillingAddressID: types.StringValue("another-random-id"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetDefaultBillingAddressAction{
- AddressId: types.StringValue("another-random-id").ValueStringPointer(),
- },
- },
- },
- },
- {
- "business unit update associate mode",
- Division{
- AssociateMode: types.StringValue("Explicit"),
- },
- Division{
- AssociateMode: types.StringValue("ExplicitAndFromParent"),
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitChangeAssociateModeAction{
- AssociateMode: "ExplicitAndFromParent",
- },
- },
- },
- },
- {
- "business unit update stores",
- Division{
- Stores: []StoreKeyReference{
- {
- Key: types.StringValue("store-1"),
- },
- {
- Key: types.StringValue("store-2"),
- },
- },
- },
- Division{
- Stores: []StoreKeyReference{
- {
- Key: types.StringValue("store-1"),
- },
- {
- Key: types.StringValue("store-3"),
- },
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitAddStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: types.StringValue("store-3").ValueStringPointer(),
- ID: nil,
- },
- },
- platform.BusinessUnitRemoveStoreAction{
- Store: platform.StoreResourceIdentifier{
- Key: types.StringValue("store-2").ValueStringPointer(),
- ID: nil,
- },
- },
- },
- },
- },
- {
- "business unit add address",
- Division{
- Addresses: []Address{},
- },
- Division{
- Addresses: []Address{
- {
- Key: types.StringValue("new-york-office"),
- Country: types.StringValue("US"),
- Salutation: types.StringValue("Mr."),
- FirstName: types.StringValue("John"),
- LastName: types.StringValue("Doe"),
- StreetName: types.StringValue("Main St."),
- StreetNumber: types.StringValue("123"),
- AdditionalStreetInfo: types.StringValue("Apt. 1"),
- PostalCode: types.StringValue("12345"),
- City: types.StringValue("New York"),
- Region: types.StringValue("New York"),
- State: types.StringValue("New York"),
- Company: types.StringValue("Example Inc."),
- Department: types.StringValue("Sales"),
- Building: types.StringValue("1"),
- Apartment: types.StringValue("1"),
- POBox: types.StringValue("123"),
- Phone: types.StringValue("1234567890"),
- Mobile: types.StringValue("1234567890"),
- Fax: types.StringValue("1234567890"),
- },
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitAddAddressAction{
- Address: platform.BaseAddress{
- Key: utils.StringRef("new-york-office"),
- Country: "US",
- Salutation: utils.StringRef("Mr."),
- FirstName: utils.StringRef("John"),
- LastName: utils.StringRef("Doe"),
- StreetName: utils.StringRef("Main St."),
- StreetNumber: utils.StringRef("123"),
- AdditionalStreetInfo: utils.StringRef("Apt. 1"),
- PostalCode: utils.StringRef("12345"),
- City: utils.StringRef("New York"),
- Region: utils.StringRef("New York"),
- State: utils.StringRef("New York"),
- Company: utils.StringRef("Example Inc."),
- Department: utils.StringRef("Sales"),
- Building: utils.StringRef("1"),
- Apartment: utils.StringRef("1"),
- POBox: utils.StringRef("123"),
- Phone: utils.StringRef("1234567890"),
- Mobile: utils.StringRef("1234567890"),
- Fax: utils.StringRef("1234567890"),
- },
- },
- },
- },
- },
- {
- "business unit remove address",
- Division{
- Addresses: []Address{
- {
- Key: types.StringValue("new-york-office"),
- Country: types.StringValue("US"),
- Salutation: types.StringValue("Mr."),
- FirstName: types.StringValue("John"),
- LastName: types.StringValue("Doe"),
- StreetName: types.StringValue("Main St."),
- StreetNumber: types.StringValue("123"),
- AdditionalStreetInfo: types.StringValue("Apt. 1"),
- PostalCode: types.StringValue("12345"),
- City: types.StringValue("New York"),
- Region: types.StringValue("New York"),
- State: types.StringValue("New York"),
- Company: types.StringValue("Example Inc."),
- Department: types.StringValue("Sales"),
- Building: types.StringValue("1"),
- Apartment: types.StringValue("1"),
- POBox: types.StringValue("123"),
- Phone: types.StringValue("1234567890"),
- Mobile: types.StringValue("1234567890"),
- Fax: types.StringValue("1234567890"),
- },
- },
- },
- Division{
- Addresses: []Address{},
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitRemoveAddressAction{
- AddressKey: utils.StringRef("new-york-office"),
- },
- },
- },
- },
- {
- "business unit set associates",
- Division{},
- Division{
- Associates: []Associate{
- {
- AssociateRoleAssignments: []AssociateRoleAssignment{
- {
- AssociateRole: AssociateRoleKeyReference{
- Key: types.StringValue("role-1"),
- },
- },
- },
- Customer: CustomerReference{
- ID: types.StringValue("customer-1"),
- },
- },
- },
- },
- platform.BusinessUnitUpdate{
- Actions: []platform.BusinessUnitUpdateAction{
- platform.BusinessUnitSetAssociatesAction{
- Associates: []platform.AssociateDraft{
- {
- AssociateRoleAssignments: []platform.AssociateRoleAssignmentDraft{
- {
- AssociateRole: platform.AssociateRoleResourceIdentifier{
- Key: utils.StringRef("role-1"),
- },
- },
- },
- Customer: platform.CustomerResourceIdentifier{
- ID: utils.StringRef("customer-1"),
- },
- },
- },
- },
- },
- },
- },
- }
-
- for _, c := range cases {
- t.Run(c.name, func(t *testing.T) {
- result := c.state.updateActions(c.plan)
- assert.EqualValues(t, c.expected, result)
- })
- }
-}
diff --git a/internal/resources/business_unit/resource.go b/internal/resources/business_unit/resource.go
deleted file mode 100644
index 6f8c6dc0..00000000
--- a/internal/resources/business_unit/resource.go
+++ /dev/null
@@ -1,934 +0,0 @@
-package business_unit
-
-import (
- "context"
- "errors"
- "time"
-
- "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
- "github.com/hashicorp/terraform-plugin-framework/path"
- "github.com/hashicorp/terraform-plugin-framework/resource"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
- "github.com/labd/commercetools-go-sdk/platform"
- "github.com/labd/terraform-provider-commercetools/internal/utils"
-)
-
-var (
- _ resource.Resource = &companyResource{}
- _ resource.ResourceWithConfigure = &companyResource{}
- _ resource.ResourceWithImportState = &companyResource{}
-)
-
-type companyResource struct {
- client *platform.ByProjectKeyRequestBuilder
-}
-
-// Schema implements resource.Resource.
-func (b *companyResource) Schema(_ context.Context, req resource.SchemaRequest, res *resource.SchemaResponse) {
- res.Schema = schema.Schema{
- Description: "Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Company from the generic BusinessUnit.\n\n" +
- "See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units",
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Unique identifier of the Company.",
- Computed: true,
- },
- "version": schema.Int64Attribute{
- Description: "The current version of the Company.",
- Computed: true,
- },
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier for the Company.",
- Required: true,
- },
- "name": schema.StringAttribute{
- Description: "The name of the Company.",
- Required: true,
- },
- "status": schema.StringAttribute{
- Description: "The status of the Company.",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(BusinessUnitActive, BusinessUnitInactive),
- },
- },
- "contact_email": schema.StringAttribute{
- Description: "The email address of the Company.",
- Optional: true,
- },
- "shipping_address_ids": schema.ListAttribute{
- Description: "List of the shipping addresses used by the Company.",
- Optional: true,
- ElementType: types.StringType,
- PlanModifiers: []planmodifier.List{
- listplanmodifier.UseStateForUnknown(),
- },
- },
- "billing_address_ids": schema.ListAttribute{
- Description: "List of the billing addresses used by the Company.",
- Optional: true,
- ElementType: types.StringType,
- PlanModifiers: []planmodifier.List{
- listplanmodifier.UseStateForUnknown(),
- },
- },
- "default_shipping_address_id": schema.StringAttribute{
- Description: "ID of the default shipping Address.",
- Optional: true,
- },
- "default_billing_address_id": schema.StringAttribute{
- Description: "ID of the default billing Address.",
- Optional: true,
- },
- },
- Blocks: map[string]schema.Block{
- "associate": schema.ListNestedBlock{
- Description: "Associates that are part of the Business Unit in specific roles",
- NestedObject: schema.NestedBlockObject{
- Blocks: map[string]schema.Block{
- "associate_role_assignments": schema.ListNestedBlock{
- Description: "Roles assigned to the Associate within a Business Unit.",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "inheritance": schema.StringAttribute{
- Description: "Determines whether the AssociateRoleAssignment can be inherited by child Business Units",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(
- AssociateRoleInheritanceEnabled,
- AssociateRoleInheritanceDisabled,
- ),
- },
- },
- },
- Blocks: map[string]schema.Block{
- "associate_role": schema.ListNestedBlock{
- Description: "Reference to an AssociateRole by its key.",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Associate Role",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Associate Role",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(AssociateRoleTypeID),
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- "address": schema.ListNestedBlock{
- Description: "Addresses used by the Business Unit.",
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- listvalidator.IsRequired(),
- },
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Unique identifier of the Address",
- Computed: true,
- },
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Address",
- Optional: true,
- },
- "external_id": schema.StringAttribute{
- Description: "ID for the contact used in an external system",
- Optional: true,
- },
- "country": schema.StringAttribute{
- Description: "Name of the country",
- Required: true,
- },
- "title": schema.StringAttribute{
- Description: "Title of the contact, for example Dr., Prof.",
- Optional: true,
- },
- "salutation": schema.StringAttribute{
- Description: "Salutation of the contact, for example Ms., Mr.",
- Optional: true,
- },
- "first_name": schema.StringAttribute{
- Description: "First name of the contact",
- Optional: true,
- },
- "last_name": schema.StringAttribute{
- Description: "Last name of the contact",
- Optional: true,
- },
- "street_name": schema.StringAttribute{
- Description: "Name of the street",
- Optional: true,
- },
- "street_number": schema.StringAttribute{
- Description: "Street number",
- Optional: true,
- },
- "additional_street_info": schema.StringAttribute{
- Description: "Further information on the street address",
- Optional: true,
- },
- "postal_code": schema.StringAttribute{
- Description: "Postal code",
- Optional: true,
- },
- "city": schema.StringAttribute{
- Description: "Name of the city",
- Optional: true,
- },
- "region": schema.StringAttribute{
- Description: "Name of the region",
- Optional: true,
- },
- "state": schema.StringAttribute{
- Description: "Name of the state",
- Optional: true,
- },
- "company": schema.StringAttribute{
- Description: "Name of the company",
- Optional: true,
- },
- "department": schema.StringAttribute{
- Description: "Name of the department",
- Optional: true,
- },
- "building": schema.StringAttribute{
- Description: "Name or number of the building",
- Optional: true,
- },
- "apartment": schema.StringAttribute{
- Description: "Name or number of the apartment",
- Optional: true,
- },
- "po_box": schema.StringAttribute{
- Description: "Post office box number",
- Optional: true,
- },
- "phone": schema.StringAttribute{
- Description: "Phone number",
- Optional: true,
- },
- "mobile": schema.StringAttribute{
- Description: "Mobile phone number",
- Optional: true,
- },
- "email": schema.StringAttribute{
- Description: "Email address",
- Optional: true,
- },
- "fax": schema.StringAttribute{
- Description: "Fax number",
- Optional: true,
- },
- "additional_address_info": schema.StringAttribute{
- Description: "Further information on the Address",
- Optional: true,
- },
- },
- },
- },
- "store": schema.ListNestedBlock{
- Description: "Stores that are part of the Business Unit.",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Store",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Store",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(StoreTypeID),
- },
- },
- },
- },
- },
- },
- }
-}
-
-// Metadata implements resource.Resource.
-func (b *companyResource) Metadata(_ context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) {
- res.TypeName = req.ProviderTypeName + "_business_unit_company"
-}
-
-// ImportState implements resource.ResourceWithImportState.
-func (b *companyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, res *resource.ImportStateResponse) {
- resource.ImportStatePassthroughID(ctx, path.Root("id"), req, res)
-}
-
-// Configure implements resource.ResourceWithConfigure.
-func (b *companyResource) Configure(ctx context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
- if req.ProviderData == nil {
- return
- }
-
- data, ok := req.ProviderData.(*utils.ProviderData)
- if !ok {
- return
- }
-
- b.client = data.Client
-}
-
-// Create implements resource.Resource.
-func (b *companyResource) Create(ctx context.Context, req resource.CreateRequest, res *resource.CreateResponse) {
- var plan Company
- diags := req.Plan.Get(ctx, &plan)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- draft := plan.draft()
-
- var bu platform.BusinessUnit
- err := retry.RetryContext(ctx, 20*time.Second, func() *retry.RetryError {
- var err error
- bu, err = b.client.BusinessUnits().Post(draft).Execute(ctx)
-
- return utils.ProcessRemoteError(err)
- })
- if err != nil {
- res.Diagnostics.AddError(
- "Error creating business unit",
- "Could not create business unit, unexpected error: "+err.Error(),
- )
- return
- }
-
- current := NewCompanyFromNative(bu)
-
- diags = res.State.Set(ctx, ¤t)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-}
-
-// Delete implements resource.Resource.
-func (b *companyResource) Delete(ctx context.Context, req resource.DeleteRequest, res *resource.DeleteResponse) {
- var state Company
-
- diags := req.State.Get(ctx, &state)
- res.Diagnostics.Append(diags...)
-
- if res.Diagnostics.HasError() {
- return
- }
-
- err := retry.RetryContext(
- ctx,
- 5*time.Second,
- func() *retry.RetryError {
- _, err := b.client.BusinessUnits().
- WithId(state.ID.ValueString()).
- Delete().
- Version(int(state.Version.ValueInt64())).
- Execute(ctx)
-
- return utils.ProcessRemoteError(err)
- },
- )
- if err != nil {
- res.Diagnostics.AddError(
- "Error deleting business unit",
- "Could not delete business unit, unexpected error: "+err.Error(),
- )
- return
- }
-}
-
-// Read implements resource.Resource.
-func (b *companyResource) Read(ctx context.Context, req resource.ReadRequest, res *resource.ReadResponse) {
- var state Company
- diags := req.State.Get(ctx, &state)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- company, err := b.client.BusinessUnits().WithId(state.ID.ValueString()).Get().Execute(ctx)
- if err != nil {
- if errors.Is(err, platform.ErrNotFound) {
- res.State.RemoveResource(ctx)
- return
- }
-
- res.Diagnostics.AddError(
- "Error reading business unit",
- "Could not retrieve the business unit, unexpected error: "+err.Error(),
- )
- return
- }
-
- current := NewCompanyFromNative(company)
-
- diags = res.State.Set(ctx, current)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-}
-
-// Update implements resource.Resource.
-func (b *companyResource) Update(ctx context.Context, req resource.UpdateRequest, res *resource.UpdateResponse) {
- var plan Company
- diags := req.Plan.Get(ctx, &plan)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- var state Company
- diags = req.State.Get(ctx, &state)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- input := state.updateActions(plan)
- var company *platform.BusinessUnit
-
- err := retry.RetryContext(ctx, 5*time.Second, func() *retry.RetryError {
- var err error
- company, err = b.client.BusinessUnits().
- WithId(state.ID.ValueString()).
- Post(input).
- Execute(ctx)
-
- return utils.ProcessRemoteError(err)
- })
- if err != nil {
- res.Diagnostics.AddError(
- "Error updating business unit",
- "Could not update business unit, unexpected error: "+err.Error(),
- )
- return
- }
-
- current := NewCompanyFromNative(company)
- diags = res.State.Set(ctx, ¤t)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-}
-
-func NewCompanyResource() resource.Resource {
- return &companyResource{}
-}
-
-var (
- _ resource.Resource = &divisionResource{}
- _ resource.ResourceWithConfigure = &divisionResource{}
- _ resource.ResourceWithImportState = &divisionResource{}
-)
-
-type divisionResource struct {
- client *platform.ByProjectKeyRequestBuilder
-}
-
-// Schema implements resource.Resource.
-func (b *divisionResource) Schema(_ context.Context, req resource.SchemaRequest, res *resource.SchemaResponse) {
- res.Schema = schema.Schema{
- Description: "Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Company from the generic BusinessUnit.\n\n" +
- "See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units",
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Unique identifier of the Company.",
- Computed: true,
- },
- "version": schema.Int64Attribute{
- Description: "The current version of the Company.",
- Computed: true,
- },
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier for the Company.",
- Required: true,
- },
- "name": schema.StringAttribute{
- Description: "The name of the Company.",
- Required: true,
- },
- "status": schema.StringAttribute{
- Description: "The status of the Company.",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(BusinessUnitActive, BusinessUnitInactive),
- },
- },
- "associate_mode": schema.StringAttribute{
- Description: "The association mode of the Company.",
- Required: true,
- },
- "contact_email": schema.StringAttribute{
- Description: "The email address of the Company.",
- Optional: true,
- },
- "shipping_address_ids": schema.ListAttribute{
- Description: "List of the shipping addresses used by the Company.",
- Optional: true,
- ElementType: types.StringType,
- PlanModifiers: []planmodifier.List{
- listplanmodifier.UseStateForUnknown(),
- },
- },
- "billing_address_ids": schema.ListAttribute{
- Description: "List of the billing addresses used by the Company.",
- Optional: true,
- ElementType: types.StringType,
- PlanModifiers: []planmodifier.List{
- listplanmodifier.UseStateForUnknown(),
- },
- },
- "default_shipping_address_id": schema.StringAttribute{
- Description: "ID of the default shipping Address.",
- Optional: true,
- },
- "default_billing_address_id": schema.StringAttribute{
- Description: "ID of the default billing Address.",
- Optional: true,
- },
- },
- Blocks: map[string]schema.Block{
- "associate": schema.ListNestedBlock{
- Description: "Associates that are part of the Business Unit in specific roles",
- NestedObject: schema.NestedBlockObject{
- Blocks: map[string]schema.Block{
- "associate_role_assignments": schema.ListNestedBlock{
- Description: "Roles assigned to the Associate within a Business Unit.",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "inheritance": schema.StringAttribute{
- Description: "Determines whether the AssociateRoleAssignment can be inherited by child Business Units",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(
- AssociateRoleInheritanceEnabled,
- AssociateRoleInheritanceDisabled,
- ),
- },
- },
- },
- Blocks: map[string]schema.Block{
- "associate_role": schema.ListNestedBlock{
- Description: "Reference to an AssociateRole by its key.",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Associate Role",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Associate Role",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(AssociateRoleTypeID),
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- "address": schema.ListNestedBlock{
- Description: "Addresses used by the Business Unit.",
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- listvalidator.IsRequired(),
- },
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Unique identifier of the Address",
- Computed: true,
- },
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Address",
- Optional: true,
- },
- "external_id": schema.StringAttribute{
- Description: "ID for the contact used in an external system",
- Optional: true,
- },
- "country": schema.StringAttribute{
- Description: "Name of the country",
- Required: true,
- },
- "title": schema.StringAttribute{
- Description: "Title of the contact, for example Dr., Prof.",
- Optional: true,
- },
- "salutation": schema.StringAttribute{
- Description: "Salutation of the contact, for example Ms., Mr.",
- Optional: true,
- },
- "first_name": schema.StringAttribute{
- Description: "First name of the contact",
- Optional: true,
- },
- "last_name": schema.StringAttribute{
- Description: "Last name of the contact",
- Optional: true,
- },
- "street_name": schema.StringAttribute{
- Description: "Name of the street",
- Optional: true,
- },
- "street_number": schema.StringAttribute{
- Description: "Street number",
- Optional: true,
- },
- "additional_street_info": schema.StringAttribute{
- Description: "Further information on the street address",
- Optional: true,
- },
- "postal_code": schema.StringAttribute{
- Description: "Postal code",
- Optional: true,
- },
- "city": schema.StringAttribute{
- Description: "Name of the city",
- Optional: true,
- },
- "region": schema.StringAttribute{
- Description: "Name of the region",
- Optional: true,
- },
- "state": schema.StringAttribute{
- Description: "Name of the state",
- Optional: true,
- },
- "company": schema.StringAttribute{
- Description: "Name of the company",
- Optional: true,
- },
- "department": schema.StringAttribute{
- Description: "Name of the department",
- Optional: true,
- },
- "building": schema.StringAttribute{
- Description: "Name or number of the building",
- Optional: true,
- },
- "apartment": schema.StringAttribute{
- Description: "Name or number of the apartment",
- Optional: true,
- },
- "po_box": schema.StringAttribute{
- Description: "Post office box number",
- Optional: true,
- },
- "phone": schema.StringAttribute{
- Description: "Phone number",
- Optional: true,
- },
- "mobile": schema.StringAttribute{
- Description: "Mobile phone number",
- Optional: true,
- },
- "email": schema.StringAttribute{
- Description: "Email address",
- Optional: true,
- },
- "fax": schema.StringAttribute{
- Description: "Fax number",
- Optional: true,
- },
- "additional_address_info": schema.StringAttribute{
- Description: "Further information on the Address",
- Optional: true,
- },
- },
- },
- },
- "parent_unit": schema.ListNestedBlock{
- Description: "Reference to a parent Business Unit by its key.",
- Validators: []validator.List{
- listvalidator.SizeAtMost(1),
- listvalidator.SizeAtLeast(1),
- listvalidator.IsRequired(),
- },
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Business Unit",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Business Unit",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(CompanyType, DivisionType),
- },
- },
- },
- },
- },
- "stores": schema.ListNestedBlock{
- Description: "Stores that are part of the Business Unit.",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Store",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Store",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(StoreTypeID),
- },
- },
- },
- },
- },
- "inherited_associates": schema.ListNestedBlock{
- Description: "Associates that are inherited from parent Business Unit",
- NestedObject: schema.NestedBlockObject{
- Blocks: map[string]schema.Block{
- "inherited_associate_role_assignments": schema.ListNestedBlock{
- Description: "Inherited roles of the Associate within a Business Unit",
- NestedObject: schema.NestedBlockObject{
- Blocks: map[string]schema.Block{
- "associate_role": schema.ListNestedBlock{
- Description: "Inherited role the Associate holds within a Business Unit",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Associate Role",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Associate Role",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(AssociateRoleTypeID),
- },
- },
- },
- },
- },
- "source": schema.ListNestedBlock{
- Description: "",
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "key": schema.StringAttribute{
- Description: "User-defined unique identifier of the Business Unit",
- Required: true,
- },
- "type_id": schema.StringAttribute{
- Description: "The type of the Business Unit",
- Required: true,
- Validators: []validator.String{
- stringvalidator.OneOf(BusinessUnitTypeID),
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- }
-}
-
-// Metadata implements resource.Resource.
-func (b *divisionResource) Metadata(_ context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) {
- res.TypeName = req.ProviderTypeName + "_business_unit_division"
-}
-
-// ImportState implements resource.ResourceWithImportState.
-func (b *divisionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, res *resource.ImportStateResponse) {
- resource.ImportStatePassthroughID(ctx, path.Root("id"), req, res)
-}
-
-// Configure implements resource.ResourceWithConfigure.
-func (b *divisionResource) Configure(ctx context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
- if req.ProviderData == nil {
- return
- }
-
- data, ok := req.ProviderData.(*utils.ProviderData)
- if !ok {
- return
- }
-
- b.client = data.Client
-}
-
-// Create implements resource.Resource.
-func (b *divisionResource) Create(ctx context.Context, req resource.CreateRequest, res *resource.CreateResponse) {
- var plan Division
- diags := req.Plan.Get(ctx, &plan)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- draft := plan.draft()
-
- var bu platform.BusinessUnit
- err := retry.RetryContext(ctx, 20*time.Second, func() *retry.RetryError {
- var err error
- bu, err = b.client.BusinessUnits().Post(draft).Execute(ctx)
-
- return utils.ProcessRemoteError(err)
- })
- if err != nil {
- res.Diagnostics.AddError(
- "Error creating business unit",
- "Could not create business unit, unexpected error: "+err.Error(),
- )
- return
- }
-
- current := NewDivisionFromNative(bu)
-
- diags = res.State.Set(ctx, ¤t)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-}
-
-// Delete implements resource.Resource.
-func (b *divisionResource) Delete(ctx context.Context, req resource.DeleteRequest, res *resource.DeleteResponse) {
- var state Division
-
- diags := req.State.Get(ctx, &state)
- res.Diagnostics.Append(diags...)
-
- if res.Diagnostics.HasError() {
- return
- }
-
- err := retry.RetryContext(
- ctx,
- 5*time.Second,
- func() *retry.RetryError {
- _, err := b.client.BusinessUnits().
- WithId(state.ID.ValueString()).
- Delete().
- Version(int(state.Version.ValueInt64())).
- Execute(ctx)
-
- return utils.ProcessRemoteError(err)
- },
- )
- if err != nil {
- res.Diagnostics.AddError(
- "Error deleting business unit",
- "Could not delete business unit, unexpected error: "+err.Error(),
- )
- return
- }
-}
-
-// Read implements resource.Resource.
-func (b *divisionResource) Read(ctx context.Context, req resource.ReadRequest, res *resource.ReadResponse) {
- var state Division
- diags := req.State.Get(ctx, &state)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- division, err := b.client.BusinessUnits().WithId(state.ID.ValueString()).Get().Execute(ctx)
- if err != nil {
- if errors.Is(err, platform.ErrNotFound) {
- res.State.RemoveResource(ctx)
- return
- }
-
- res.Diagnostics.AddError(
- "Error reading business unit",
- "Could not retrieve the business unit, unexpected error: "+err.Error(),
- )
- return
- }
-
- current := NewDivisionFromNative(division)
-
- diags = res.State.Set(ctx, current)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-}
-
-// Update implements resource.Resource.
-func (b *divisionResource) Update(ctx context.Context, req resource.UpdateRequest, res *resource.UpdateResponse) {
- var plan Division
- diags := req.Plan.Get(ctx, &plan)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- var state Division
- diags = req.State.Get(ctx, &state)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-
- input := state.updateActions(plan)
- var division *platform.BusinessUnit
-
- err := retry.RetryContext(ctx, 5*time.Second, func() *retry.RetryError {
- var err error
- division, err = b.client.BusinessUnits().
- WithId(state.ID.ValueString()).
- Post(input).
- Execute(ctx)
-
- return utils.ProcessRemoteError(err)
- })
- if err != nil {
- res.Diagnostics.AddError(
- "Error updating business unit",
- "Could not update business unit, unexpected error: "+err.Error(),
- )
- return
- }
-
- current := NewDivisionFromNative(division)
- diags = res.State.Set(ctx, ¤t)
- res.Diagnostics.Append(diags...)
- if res.Diagnostics.HasError() {
- return
- }
-}
-
-// NewDivisionResource creates a new resource for the Division type.
-func NewDivisionResource() resource.Resource {
- return &divisionResource{}
-}
diff --git a/internal/resources/business_unit_company/model.go b/internal/resources/business_unit_company/model.go
new file mode 100644
index 00000000..6927d481
--- /dev/null
+++ b/internal/resources/business_unit_company/model.go
@@ -0,0 +1,316 @@
+package business_unit_company
+
+import (
+ "fmt"
+ "github.com/elliotchance/pie/v2"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/labd/commercetools-go-sdk/platform"
+ "github.com/labd/terraform-provider-commercetools/internal/sharedtypes"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+ "reflect"
+ "slices"
+ "sort"
+)
+
+// Company is a type to model the fields that all types of Companies have in common.
+type Company struct {
+ ID types.String `tfsdk:"id"`
+ Version types.Int64 `tfsdk:"version"`
+ Key types.String `tfsdk:"key"`
+ Status types.String `tfsdk:"status"`
+ Name types.String `tfsdk:"name"`
+ ContactEmail types.String `tfsdk:"contact_email"`
+ ShippingAddressKeys []types.String `tfsdk:"shipping_address_keys"`
+ DefaultShippingAddressKey types.String `tfsdk:"default_shipping_address_key"`
+ BillingAddressKeys []types.String `tfsdk:"billing_address_keys"`
+ DefaultBillingAddressKey types.String `tfsdk:"default_billing_address_key"`
+ Stores []sharedtypes.StoreKeyReference `tfsdk:"store"`
+ Addresses []sharedtypes.Address `tfsdk:"address"`
+}
+
+func (c *Company) draft() (platform.CompanyDraft, error) {
+ status := platform.BusinessUnitStatus(c.Status.ValueString())
+ storeMode := platform.BusinessUnitStoreModeExplicit
+ associateMode := platform.BusinessUnitAssociateModeExplicit
+ approvalRuleMode := platform.BusinessUnitApprovalRuleModeExplicit
+
+ var addresses []platform.BaseAddress
+ for _, a := range c.Addresses {
+ addresses = append(addresses, a.Draft())
+ }
+
+ var stores []platform.StoreResourceIdentifier
+ for _, s := range c.Stores {
+ stores = append(stores, platform.StoreResourceIdentifier{
+ Key: s.Key.ValueStringPointer(),
+ })
+ }
+
+ var shippingAddressIndexes []int
+ for _, key := range c.ShippingAddressKeys {
+ i := slices.IndexFunc(c.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == key.ValueString()
+ })
+
+ if i == -1 {
+ return platform.CompanyDraft{}, fmt.Errorf("shipping address key %s is not in addresses", key.ValueString())
+ }
+
+ shippingAddressIndexes = append(shippingAddressIndexes, i)
+ }
+
+ var billingAddressIndexes []int
+ for _, key := range c.BillingAddressKeys {
+ i := slices.IndexFunc(c.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == key.ValueString()
+ })
+
+ if i == -1 {
+ return platform.CompanyDraft{}, fmt.Errorf("billing address key %s is not in addresses", key.ValueString())
+ }
+
+ billingAddressIndexes = append(billingAddressIndexes, i)
+ }
+
+ var defaultBillingAddressIndex *int
+ if !c.DefaultBillingAddressKey.IsNull() {
+ i := slices.IndexFunc(c.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == c.DefaultBillingAddressKey.ValueString()
+ })
+
+ if i == -1 {
+ return platform.CompanyDraft{}, fmt.Errorf("default billing address key %s is not in addresses", c.DefaultBillingAddressKey.ValueString())
+ }
+
+ defaultBillingAddressIndex = &i
+ }
+
+ var defaultShippingAddressIndex *int
+ if !c.DefaultShippingAddressKey.IsNull() {
+ i := slices.IndexFunc(c.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == c.DefaultShippingAddressKey.ValueString()
+ })
+
+ if i == -1 {
+ return platform.CompanyDraft{}, fmt.Errorf("default shipping address key %s is not in addresses", c.DefaultShippingAddressKey.ValueString())
+ }
+
+ defaultShippingAddressIndex = &i
+ }
+
+ return platform.CompanyDraft{
+ Key: c.Key.ValueString(),
+ Status: &status,
+ StoreMode: &storeMode,
+ AssociateMode: &associateMode,
+ ApprovalRuleMode: &approvalRuleMode,
+ Stores: stores,
+ Name: c.Name.ValueString(),
+ ContactEmail: c.ContactEmail.ValueStringPointer(),
+ Addresses: addresses,
+ ShippingAddresses: shippingAddressIndexes,
+ BillingAddresses: billingAddressIndexes,
+ DefaultShippingAddress: defaultShippingAddressIndex,
+ DefaultBillingAddress: defaultBillingAddressIndex,
+ }, nil
+}
+
+func (c *Company) updateActions(plan Company) (platform.BusinessUnitUpdate, error) {
+ result := platform.BusinessUnitUpdate{
+ Version: int(c.Version.ValueInt64()),
+ Actions: []platform.BusinessUnitUpdateAction{},
+ }
+
+ if !c.Name.Equal(plan.Name) {
+ result.Actions = append(result.Actions, platform.BusinessUnitChangeNameAction{
+ Name: plan.Name.ValueString(),
+ })
+ }
+
+ if !c.ContactEmail.Equal(plan.ContactEmail) {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetContactEmailAction{
+ ContactEmail: plan.ContactEmail.ValueStringPointer(),
+ })
+ }
+
+ if !c.Status.Equal(plan.Status) {
+ result.Actions = append(result.Actions, platform.BusinessUnitChangeStatusAction{
+ Status: plan.Status.ValueString(),
+ })
+ }
+
+ if !reflect.DeepEqual(c.Stores, plan.Stores) {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetStoresAction{
+ Stores: pie.Map(plan.Stores, func(s sharedtypes.StoreKeyReference) platform.StoreResourceIdentifier {
+ return platform.StoreResourceIdentifier{
+ Key: s.Key.ValueStringPointer(),
+ }
+ }),
+ })
+ }
+
+ if !reflect.DeepEqual(c.Addresses, plan.Addresses) {
+ addressAddActions := sharedtypes.AddressesAddActions(c.Addresses, plan.Addresses)
+ for _, action := range addressAddActions {
+ result.Actions = append(result.Actions, action)
+ }
+
+ addressChangeActions := sharedtypes.AddressesChangeActions(c.Addresses, plan.Addresses)
+ for _, action := range addressChangeActions {
+ result.Actions = append(result.Actions, action)
+ }
+ }
+
+ if !c.DefaultShippingAddressKey.Equal(plan.DefaultShippingAddressKey) {
+ if !pie.Contains(plan.ShippingAddressKeys, plan.DefaultShippingAddressKey) {
+ return result, fmt.Errorf("default shipping address key %s is not in shipping address keys", plan.DefaultShippingAddressKey.ValueString())
+ }
+
+ result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultShippingAddressAction{
+ AddressKey: plan.DefaultShippingAddressKey.ValueStringPointer(),
+ })
+ }
+
+ if !c.DefaultBillingAddressKey.Equal(plan.DefaultBillingAddressKey) {
+ if !pie.Contains(plan.BillingAddressKeys, plan.DefaultBillingAddressKey) {
+ return result, fmt.Errorf("default shipping address key %s is not in shipping address keys", plan.DefaultBillingAddressKey.ValueString())
+ }
+
+ result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultBillingAddressAction{
+ AddressKey: plan.DefaultBillingAddressKey.ValueStringPointer(),
+ })
+ }
+
+ if !reflect.DeepEqual(c.ShippingAddressKeys, plan.ShippingAddressKeys) {
+ // find shipping addresses to be added
+ for _, i := range plan.ShippingAddressKeys {
+ if !pie.Contains(c.ShippingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitAddShippingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+
+ // find shipping addresses to be removed
+ for _, i := range c.ShippingAddressKeys {
+ if !pie.Contains(plan.ShippingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitRemoveShippingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+ }
+
+ if !reflect.DeepEqual(c.BillingAddressKeys, plan.BillingAddressKeys) {
+ // find billing addresses to be added
+ for _, i := range plan.BillingAddressKeys {
+ if !pie.Contains(c.BillingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitAddBillingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+
+ // find billing addresses to be removed
+ for _, i := range c.BillingAddressKeys {
+ if !pie.Contains(plan.BillingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitRemoveBillingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+ }
+
+ // We need to delete addresses only after we have removed keys
+ if !reflect.DeepEqual(c.Addresses, plan.Addresses) {
+ addressDeleteActions := sharedtypes.AddressesDeleteActions(c.Addresses, plan.Addresses)
+ for _, action := range addressDeleteActions {
+ result.Actions = append(result.Actions, action)
+ }
+ }
+
+ return result, nil
+}
+
+// NewCompanyFromNative creates a new Company from a platform.Company.
+func NewCompanyFromNative(bu *platform.BusinessUnit) (Company, error) {
+ data, ok := (*bu).(map[string]interface{})
+ if !ok {
+ return Company{}, fmt.Errorf("failed to convert business unit to map")
+ }
+ var c platform.Company
+ err := utils.DecodeStruct(data, &c)
+ if err != nil {
+ return Company{}, err
+ }
+
+ var defaultShippingAddressKey *string
+ if c.DefaultShippingAddressId != nil {
+ i := slices.IndexFunc(c.Addresses, func(a platform.Address) bool {
+ return *a.ID == *c.DefaultShippingAddressId
+ })
+ defaultShippingAddressKey = c.Addresses[i].Key
+ }
+ var defaultBillingAddressKey *string
+ if c.DefaultBillingAddressId != nil {
+ i := slices.IndexFunc(c.Addresses, func(a platform.Address) bool {
+ return *a.ID == *c.DefaultBillingAddressId
+ })
+ defaultBillingAddressKey = c.Addresses[i].Key
+ }
+
+ var shippingAddressKeys []types.String
+ for _, id := range c.ShippingAddressIds {
+ i := slices.IndexFunc(c.Addresses, func(a platform.Address) bool {
+ return *a.ID == id
+ })
+ shippingAddressKeys = append(shippingAddressKeys, types.StringPointerValue(c.Addresses[i].Key))
+ }
+
+ var billingAddressKeys []types.String
+ for _, id := range c.BillingAddressIds {
+ i := slices.IndexFunc(c.Addresses, func(a platform.Address) bool {
+ return *a.ID == id
+ })
+ billingAddressKeys = append(billingAddressKeys, types.StringPointerValue(c.Addresses[i].Key))
+ }
+
+ var stores []sharedtypes.StoreKeyReference
+ for _, s := range c.Stores {
+ stores = append(stores, sharedtypes.NewStoreKeyReferenceFromNative(&s))
+ }
+
+ var addresses []sharedtypes.Address
+ for _, a := range c.Addresses {
+ addresses = append(addresses, sharedtypes.NewAddressFromNative(&a))
+ }
+
+ company := Company{
+ ID: types.StringValue(c.ID),
+ Version: types.Int64Value(int64(c.Version)),
+ Key: types.StringValue(c.Key),
+ Name: types.StringValue(c.Name),
+ Status: types.StringValue(string(c.Status)),
+ ContactEmail: types.StringPointerValue(c.ContactEmail),
+ DefaultShippingAddressKey: types.StringPointerValue(defaultShippingAddressKey),
+ DefaultBillingAddressKey: types.StringPointerValue(defaultBillingAddressKey),
+ Stores: stores,
+ Addresses: addresses,
+ ShippingAddressKeys: shippingAddressKeys,
+ BillingAddressKeys: billingAddressKeys,
+ }
+
+ sort.Slice(company.Addresses, func(i, j int) bool {
+ return company.Addresses[i].Key.ValueString() < company.Addresses[j].Key.ValueString()
+ })
+
+ sort.Slice(company.ShippingAddressKeys, func(i, j int) bool {
+ return company.ShippingAddressKeys[i].ValueString() < company.ShippingAddressKeys[j].ValueString()
+ })
+
+ sort.Slice(company.BillingAddressKeys, func(i, j int) bool {
+ return company.BillingAddressKeys[i].ValueString() < company.BillingAddressKeys[j].ValueString()
+ })
+
+ return company, nil
+}
diff --git a/internal/resources/business_unit_company/model_test.go b/internal/resources/business_unit_company/model_test.go
new file mode 100644
index 00000000..020cfa68
--- /dev/null
+++ b/internal/resources/business_unit_company/model_test.go
@@ -0,0 +1,480 @@
+package business_unit_company
+
+import (
+ "github.com/labd/terraform-provider-commercetools/internal/sharedtypes"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/labd/commercetools-go-sdk/platform"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBusinessUnit_Company_Draft(t *testing.T) {
+ cases := []struct {
+ name string
+ company Company
+ expected platform.CompanyDraft
+ }{
+ {
+ name: "Basic company draft",
+ company: Company{
+ Key: types.StringValue("company-key"),
+ Status: types.StringValue("Active"),
+ Name: types.StringValue("Company Name"),
+ ContactEmail: types.StringValue("contact@example.com"),
+ Addresses: []sharedtypes.Address{
+ {
+ Key: types.StringValue("address-1"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("New York"),
+ },
+ {
+ Key: types.StringValue("address-2"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("Detroit"),
+ },
+ },
+ Stores: []sharedtypes.StoreKeyReference{
+ {Key: types.StringValue("store-1")},
+ },
+ ShippingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ BillingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ DefaultBillingAddressKey: types.StringValue("address-2"),
+ DefaultShippingAddressKey: types.StringValue("address-2"),
+ },
+ expected: platform.CompanyDraft{
+ Key: "company-key",
+ Status: utils.Ref(platform.BusinessUnitStatusActive),
+ Name: "Company Name",
+ StoreMode: utils.Ref(platform.BusinessUnitStoreModeExplicit),
+ AssociateMode: utils.Ref(platform.BusinessUnitAssociateModeExplicit),
+ ApprovalRuleMode: utils.Ref(platform.BusinessUnitApprovalRuleModeExplicit),
+ ContactEmail: utils.Ref("contact@example.com"),
+ Addresses: []platform.BaseAddress{
+ {
+ Key: utils.Ref("address-1"),
+ Country: "US",
+ City: utils.Ref("New York"),
+ },
+ {
+ Key: utils.Ref("address-2"),
+ Country: "US",
+ City: utils.Ref("Detroit"),
+ },
+ },
+ Stores: []platform.StoreResourceIdentifier{
+ {
+ Key: utils.Ref("store-1"),
+ },
+ },
+ DefaultShippingAddress: utils.Ref(1),
+ DefaultBillingAddress: utils.Ref(1),
+ ShippingAddresses: []int{0, 1},
+ BillingAddresses: []int{0, 1},
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ result, err := c.company.draft()
+ assert.NoError(t, err)
+ assert.Equal(t, c.expected, result)
+ })
+ }
+}
+
+func TestBusinessUnit_Company_UpdateActions(t *testing.T) {
+ cases := []struct {
+ name string
+ state Company
+ plan Company
+ expected platform.BusinessUnitUpdate
+ }{
+ {
+ "business unit update name",
+ Company{
+ Name: types.StringValue("Example business unit"),
+ },
+ Company{
+ Name: types.StringValue("Updated business unit"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitChangeNameAction{
+ Name: "Updated business unit",
+ },
+ },
+ },
+ },
+ {
+ "business unit update contact email",
+ Company{
+ ContactEmail: types.StringValue("info@example.com"),
+ },
+ Company{
+ ContactEmail: types.StringValue("new@example.com"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetContactEmailAction{
+ ContactEmail: types.StringValue("new@example.com").ValueStringPointer(),
+ },
+ },
+ },
+ },
+ {
+ "business unit update status",
+ Company{
+ Status: types.StringValue("Active"),
+ },
+ Company{
+ Status: types.StringValue("Inactive"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitChangeStatusAction{
+ Status: "Inactive",
+ },
+ },
+ },
+ },
+ {
+ "business unit update default shipping address",
+ Company{
+ ShippingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultShippingAddressKey: types.StringValue("some-random-id"),
+ },
+ Company{
+ ShippingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultShippingAddressKey: types.StringValue("another-random-id"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetDefaultShippingAddressAction{
+ AddressKey: types.StringValue("another-random-id").ValueStringPointer(),
+ },
+ },
+ },
+ },
+ {
+ "business unit update default billing address",
+ Company{
+ BillingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultBillingAddressKey: types.StringValue("some-random-id"),
+ },
+ Company{
+ BillingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultBillingAddressKey: types.StringValue("another-random-id"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetDefaultBillingAddressAction{
+ AddressKey: types.StringValue("another-random-id").ValueStringPointer(),
+ },
+ },
+ },
+ },
+ {
+ "business unit update stores",
+ Company{
+ Stores: []sharedtypes.StoreKeyReference{
+ {
+ Key: types.StringValue("store-1"),
+ },
+ {
+ Key: types.StringValue("store-2"),
+ },
+ },
+ },
+ Company{
+ Stores: []sharedtypes.StoreKeyReference{
+ {
+ Key: types.StringValue("store-1"),
+ },
+ {
+ Key: types.StringValue("store-3"),
+ },
+ },
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetStoresAction{
+ Stores: []platform.StoreResourceIdentifier{
+ {
+ Key: types.StringValue("store-1").ValueStringPointer(),
+ ID: nil,
+ },
+ {
+ Key: types.StringValue("store-3").ValueStringPointer(),
+ ID: nil,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "business unit add address",
+ Company{
+ Addresses: []sharedtypes.Address{},
+ },
+ Company{
+ Addresses: []sharedtypes.Address{
+ {
+ Key: types.StringValue("new-york-office"),
+ Country: types.StringValue("US"),
+ Salutation: types.StringValue("Mr."),
+ FirstName: types.StringValue("John"),
+ LastName: types.StringValue("Doe"),
+ StreetName: types.StringValue("Main St."),
+ StreetNumber: types.StringValue("123"),
+ AdditionalStreetInfo: types.StringValue("Apt. 1"),
+ PostalCode: types.StringValue("12345"),
+ City: types.StringValue("New York"),
+ Region: types.StringValue("New York"),
+ State: types.StringValue("New York"),
+ Company: types.StringValue("Example Inc."),
+ Department: types.StringValue("Sales"),
+ Building: types.StringValue("1"),
+ Apartment: types.StringValue("1"),
+ POBox: types.StringValue("123"),
+ Phone: types.StringValue("1234567890"),
+ Mobile: types.StringValue("1234567890"),
+ Fax: types.StringValue("1234567890"),
+ },
+ },
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitAddAddressAction{
+ Address: platform.BaseAddress{
+ Key: utils.StringRef("new-york-office"),
+ Country: "US",
+ Salutation: utils.StringRef("Mr."),
+ FirstName: utils.StringRef("John"),
+ LastName: utils.StringRef("Doe"),
+ StreetName: utils.StringRef("Main St."),
+ StreetNumber: utils.StringRef("123"),
+ AdditionalStreetInfo: utils.StringRef("Apt. 1"),
+ PostalCode: utils.StringRef("12345"),
+ City: utils.StringRef("New York"),
+ Region: utils.StringRef("New York"),
+ State: utils.StringRef("New York"),
+ Company: utils.StringRef("Example Inc."),
+ Department: utils.StringRef("Sales"),
+ Building: utils.StringRef("1"),
+ Apartment: utils.StringRef("1"),
+ POBox: utils.StringRef("123"),
+ Phone: utils.StringRef("1234567890"),
+ Mobile: utils.StringRef("1234567890"),
+ Fax: utils.StringRef("1234567890"),
+ },
+ },
+ },
+ },
+ },
+ {
+ "business unit remove address",
+ Company{
+ Addresses: []sharedtypes.Address{
+ {
+ Key: types.StringValue("new-york-office"),
+ Country: types.StringValue("US"),
+ Salutation: types.StringValue("Mr."),
+ FirstName: types.StringValue("John"),
+ LastName: types.StringValue("Doe"),
+ StreetName: types.StringValue("Main St."),
+ StreetNumber: types.StringValue("123"),
+ AdditionalStreetInfo: types.StringValue("Apt. 1"),
+ PostalCode: types.StringValue("12345"),
+ City: types.StringValue("New York"),
+ Region: types.StringValue("New York"),
+ State: types.StringValue("New York"),
+ Company: types.StringValue("Example Inc."),
+ Department: types.StringValue("Sales"),
+ Building: types.StringValue("1"),
+ Apartment: types.StringValue("1"),
+ POBox: types.StringValue("123"),
+ Phone: types.StringValue("1234567890"),
+ Mobile: types.StringValue("1234567890"),
+ Fax: types.StringValue("1234567890"),
+ },
+ },
+ },
+ Company{
+ Addresses: []sharedtypes.Address{},
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitRemoveAddressAction{
+ AddressKey: utils.StringRef("new-york-office"),
+ },
+ },
+ },
+ },
+ {
+ "business unit add billing address id",
+ Company{
+ BillingAddressKeys: []types.String{},
+ },
+ Company{
+ BillingAddressKeys: []types.String{
+ types.StringValue("new-york-office"),
+ },
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitAddBillingAddressIdAction{
+ AddressKey: utils.StringRef("new-york-office"),
+ },
+ },
+ },
+ },
+ {
+ "business unit remove billing address id",
+ Company{
+ BillingAddressKeys: []types.String{
+ types.StringValue("new-york-office"),
+ },
+ },
+ Company{
+ BillingAddressKeys: []types.String{},
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitRemoveBillingAddressIdAction{
+ AddressKey: utils.StringRef("new-york-office"),
+ },
+ },
+ },
+ },
+ {
+ "business unit add shipping address id",
+ Company{
+ ShippingAddressKeys: []types.String{},
+ },
+ Company{
+ ShippingAddressKeys: []types.String{
+ types.StringValue("new-york-office"),
+ },
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitAddShippingAddressIdAction{
+ AddressKey: utils.StringRef("new-york-office"),
+ },
+ },
+ },
+ },
+ {
+ "business unit remove shipping address id",
+ Company{
+ ShippingAddressKeys: []types.String{
+ types.StringValue("new-york-office"),
+ },
+ },
+ Company{
+ ShippingAddressKeys: []types.String{},
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitRemoveShippingAddressIdAction{
+ AddressKey: utils.StringRef("new-york-office"),
+ },
+ },
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ result, err := c.state.updateActions(c.plan)
+ assert.NoError(t, err)
+ assert.EqualValues(t, c.expected, result)
+ })
+ }
+}
+
+func TestBusinessUnit_Company_NewCompanyFromNative(t *testing.T) {
+ cases := []struct {
+ name string
+ company map[string]interface{}
+ expected Company
+ }{
+ {
+ name: "Basic company draft",
+ company: map[string]interface{}{
+ "id": "company-id",
+ "key": "company-key",
+ "version": 1,
+ "status": platform.BusinessUnitStatusActive,
+ "name": "Company Name",
+ "contactEmail": utils.Ref("contact@example.com"),
+ "addresses": []map[string]interface{}{
+ {
+ "id": utils.Ref("address-id-1"),
+ "key": utils.Ref("address-1"),
+ "country": "US",
+ "city": utils.Ref("New York"),
+ },
+ {
+ "id": utils.Ref("address-id-2"),
+ "key": utils.Ref("address-2"),
+ "country": "US",
+ "city": utils.Ref("Detroit"),
+ },
+ },
+ "stores": []map[string]interface{}{
+ {"key": "store-1"},
+ },
+ "shippingAddressIds": []string{"address-id-1", "address-id-2"},
+ "billingAddressIds": []string{"address-id-1", "address-id-2"},
+ "defaultBillingAddressId": utils.Ref("address-id-2"),
+ "defaultShippingAddressId": utils.Ref("address-id-2"),
+ },
+ expected: Company{
+ ID: types.StringValue("company-id"),
+ Key: types.StringValue("company-key"),
+ Version: types.Int64Value(1),
+ Status: types.StringValue("Active"),
+ Name: types.StringValue("Company Name"),
+ ContactEmail: types.StringValue("contact@example.com"),
+ Addresses: []sharedtypes.Address{
+ {
+ ID: types.StringValue("address-id-1"),
+ Key: types.StringValue("address-1"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("New York"),
+ },
+ {
+ ID: types.StringValue("address-id-2"),
+ Key: types.StringValue("address-2"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("Detroit"),
+ },
+ },
+ Stores: []sharedtypes.StoreKeyReference{
+ {Key: types.StringValue("store-1")},
+ },
+ ShippingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ BillingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ DefaultBillingAddressKey: types.StringValue("address-2"),
+ DefaultShippingAddressKey: types.StringValue("address-2"),
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ var data platform.BusinessUnit
+ err := utils.DecodeStruct(c.company, &data)
+ assert.NoError(t, err)
+
+ result, err := NewCompanyFromNative(&data)
+ assert.NoError(t, err)
+ assert.Equal(t, c.expected, result)
+ })
+ }
+}
diff --git a/internal/resources/business_unit_company/resource.go b/internal/resources/business_unit_company/resource.go
new file mode 100644
index 00000000..4a725037
--- /dev/null
+++ b/internal/resources/business_unit_company/resource.go
@@ -0,0 +1,310 @@
+package business_unit_company
+
+import (
+ "context"
+ "errors"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
+ "github.com/labd/terraform-provider-commercetools/internal/sharedtypes"
+ "regexp"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
+ "github.com/labd/commercetools-go-sdk/platform"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+)
+
+var (
+ _ resource.Resource = &companyResource{}
+ _ resource.ResourceWithConfigure = &companyResource{}
+ _ resource.ResourceWithImportState = &companyResource{}
+)
+
+type companyResource struct {
+ client *platform.ByProjectKeyRequestBuilder
+}
+
+func NewCompanyResource() resource.Resource {
+ return &companyResource{}
+}
+
+// Schema implements resource.Resource.
+func (b *companyResource) Schema(_ context.Context, req resource.SchemaRequest, res *resource.SchemaResponse) {
+ res.Schema = schema.Schema{
+ MarkdownDescription: "Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Company from the generic BusinessUnit.\n\n" +
+ "See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "Unique identifier of the Company.",
+ Computed: true,
+ },
+ "version": schema.Int64Attribute{
+ MarkdownDescription: "The current version of the Company.",
+ Computed: true,
+ },
+ "key": schema.StringAttribute{
+ MarkdownDescription: "User-defined unique identifier for the Company.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.LengthBetween(2, 256),
+ stringvalidator.RegexMatches(
+ regexp.MustCompile("^[A-Za-z0-9_-]+$"),
+ "Key must match pattern ^[A-Za-z0-9_-]+$",
+ ),
+ },
+ },
+ "status": schema.StringAttribute{
+ MarkdownDescription: "The status of the Company.",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(string(platform.BusinessUnitStatusActive)),
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ string(platform.BusinessUnitStatusActive),
+ string(platform.BusinessUnitStatusInactive),
+ ),
+ },
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "The name of the Company.",
+ Required: true,
+ },
+ "contact_email": schema.StringAttribute{
+ MarkdownDescription: "The email address of the Company.",
+ Optional: true,
+ },
+ "shipping_address_keys": schema.SetAttribute{
+ MarkdownDescription: "Indexes of entries in addresses to set as shipping addresses. The shippingAddressIds of the [Customer](https://docs.commercetools.com/api/projects/customers) will be replaced by these addresses.",
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "default_shipping_address_key": schema.StringAttribute{
+ MarkdownDescription: "Index of the entry in addresses to set as the default shipping address.",
+ Optional: true,
+ },
+ "billing_address_keys": schema.SetAttribute{
+ MarkdownDescription: "Indexes of entries in addresses to set as billing addresses. The billingAddressIds of the [Customer](https://docs.commercetools.com/api/projects/customers) will be replaced by these addresses.",
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "default_billing_address_key": schema.StringAttribute{
+ MarkdownDescription: "Index of the entry in addresses to set as the default billing address.",
+ Optional: true,
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "store": sharedtypes.StoreKeyReferenceBlockSchema,
+ "address": sharedtypes.AddressBlockSchema,
+ },
+ }
+}
+
+// Metadata implements resource.Resource.
+func (b *companyResource) Metadata(_ context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) {
+ res.TypeName = req.ProviderTypeName + "_business_unit_company"
+}
+
+// ImportState implements resource.ResourceWithImportState.
+func (b *companyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, res *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, res)
+}
+
+// Configure implements resource.ResourceWithConfigure.
+func (b *companyResource) Configure(ctx context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ data, ok := req.ProviderData.(*utils.ProviderData)
+ if !ok {
+ return
+ }
+
+ b.client = data.Client
+}
+
+// Create implements resource.Resource.
+func (b *companyResource) Create(ctx context.Context, req resource.CreateRequest, res *resource.CreateResponse) {
+ var plan Company
+ diags := req.Plan.Get(ctx, &plan)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ draft, err := plan.draft()
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error creating business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ var bu *platform.BusinessUnit
+ err = retry.RetryContext(ctx, 20*time.Second, func() *retry.RetryError {
+ var err error
+ bu, err = b.client.BusinessUnits().Post(draft).Execute(ctx)
+
+ return utils.ProcessRemoteError(err)
+ })
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error creating business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ current, err := NewCompanyFromNative(bu)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error mapping business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ diags = res.State.Set(ctx, ¤t)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Read implements resource.Resource.
+func (b *companyResource) Read(ctx context.Context, req resource.ReadRequest, res *resource.ReadResponse) {
+ var state Company
+ diags := req.State.Get(ctx, &state)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ bu, err := b.client.BusinessUnits().WithId(state.ID.ValueString()).Get().Execute(ctx)
+ if err != nil {
+ if errors.Is(err, platform.ErrNotFound) {
+ res.State.RemoveResource(ctx)
+ return
+ }
+
+ res.Diagnostics.AddError(
+ "Error reading business unit",
+ "Could not retrieve the business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ current, err := NewCompanyFromNative(bu)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error mapping business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ diags = res.State.Set(ctx, current)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Update implements resource.Resource.
+func (b *companyResource) Update(ctx context.Context, req resource.UpdateRequest, res *resource.UpdateResponse) {
+ var plan Company
+ diags := req.Plan.Get(ctx, &plan)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ var state Company
+ diags = req.State.Get(ctx, &state)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ input, err := state.updateActions(plan)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error updating business unit",
+ "Could not update business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ var bu *platform.BusinessUnit
+
+ err = retry.RetryContext(ctx, 5*time.Second, func() *retry.RetryError {
+ var err error
+ bu, err = b.client.BusinessUnits().
+ WithId(state.ID.ValueString()).
+ Post(input).
+ Execute(ctx)
+
+ return utils.ProcessRemoteError(err)
+ })
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error updating business unit",
+ "Could not update business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ current, err := NewCompanyFromNative(bu)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error mapping business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ diags = res.State.Set(ctx, ¤t)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Delete implements resource.Resource.
+func (b *companyResource) Delete(ctx context.Context, req resource.DeleteRequest, res *resource.DeleteResponse) {
+ var state Company
+
+ diags := req.State.Get(ctx, &state)
+ res.Diagnostics.Append(diags...)
+
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ err := retry.RetryContext(
+ ctx,
+ 5*time.Second,
+ func() *retry.RetryError {
+ _, err := b.client.BusinessUnits().
+ WithId(state.ID.ValueString()).
+ Delete().
+ Version(int(state.Version.ValueInt64())).
+ Execute(ctx)
+
+ return utils.ProcessRemoteError(err)
+ },
+ )
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error deleting business unit",
+ "Could not delete business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+}
diff --git a/internal/resources/business_unit/resource_test.go b/internal/resources/business_unit_company/resource_test.go
similarity index 82%
rename from internal/resources/business_unit/resource_test.go
rename to internal/resources/business_unit_company/resource_test.go
index ad3b3ff2..a8273a1b 100644
--- a/internal/resources/business_unit/resource_test.go
+++ b/internal/resources/business_unit_company/resource_test.go
@@ -1,46 +1,27 @@
-package business_unit_test
+package business_unit_company_test
import (
"context"
+ "github.com/labd/terraform-provider-commercetools/internal/resources/business_unit_company"
"testing"
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+
"github.com/labd/terraform-provider-commercetools/internal/acctest"
- "github.com/labd/terraform-provider-commercetools/internal/resources/business_unit"
"github.com/labd/terraform-provider-commercetools/internal/utils"
)
-func TestCompanySchemaImplementation(t *testing.T) {
- t.Parallel()
-
- ctx := context.Background()
- schemaRequest := fwresource.SchemaRequest{}
- schemaResponse := &fwresource.SchemaResponse{}
-
- business_unit.NewCompanyResource().Schema(ctx, schemaRequest, schemaResponse)
-
- if schemaResponse.Diagnostics.HasError() {
- t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
- }
-
- // schema validation
- diagnostics := schemaResponse.Schema.ValidateImplementation(ctx)
-
- if diagnostics.HasError() {
- t.Fatalf("Schema validation diagnostics: %+v", diagnostics)
- }
-}
-
-func TestDivisionSchemaImplementation(t *testing.T) {
+func TestBusinessUnitCompanySchemaImplementation(t *testing.T) {
t.Parallel()
ctx := context.Background()
schemaRequest := fwresource.SchemaRequest{}
schemaResponse := &fwresource.SchemaResponse{}
- business_unit.NewDivisionResource().Schema(ctx, schemaRequest, schemaResponse)
+ business_unit_company.NewCompanyResource().Schema(ctx, schemaRequest, schemaResponse)
if schemaResponse.Diagnostics.HasError() {
t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
@@ -54,7 +35,7 @@ func TestDivisionSchemaImplementation(t *testing.T) {
}
}
-func TestBusinessUnitResource_Company(t *testing.T) {
+func TestBusinessUnitResource(t *testing.T) {
r := "commercetools_business_unit_company.acme_company"
resource.Test(t, resource.TestCase{
@@ -114,12 +95,10 @@ func businessUnitTFResourceDef(name, status, email string) string {
store {
key = "acme-usa"
- type_id = "store"
}
store {
key = "acme-germany"
- type_id = "store"
}
address {
diff --git a/internal/resources/business_unit_division/model.go b/internal/resources/business_unit_division/model.go
new file mode 100644
index 00000000..d402fce3
--- /dev/null
+++ b/internal/resources/business_unit_division/model.go
@@ -0,0 +1,362 @@
+package business_unit_division
+
+import (
+ "fmt"
+ "github.com/labd/terraform-provider-commercetools/internal/sharedtypes"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+ "reflect"
+ "slices"
+ "sort"
+
+ "github.com/elliotchance/pie/v2"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/labd/commercetools-go-sdk/platform"
+)
+
+type Division struct {
+ ID types.String `tfsdk:"id"`
+ Version types.Int64 `tfsdk:"version"`
+ Key types.String `tfsdk:"key"`
+ Status types.String `tfsdk:"status"`
+ StoreMode types.String `tfsdk:"store_mode"`
+ ApprovalRuleMode types.String `tfsdk:"approval_rule_mode"`
+ Name types.String `tfsdk:"name"`
+ ContactEmail types.String `tfsdk:"contact_email"`
+ AssociateMode types.String `tfsdk:"associate_mode"`
+ ShippingAddressKeys []types.String `tfsdk:"shipping_address_keys"`
+ DefaultShippingAddressKey types.String `tfsdk:"default_shipping_address_key"`
+ BillingAddressKeys []types.String `tfsdk:"billing_address_keys"`
+ DefaultBillingAddressKey types.String `tfsdk:"default_billing_address_key"`
+ ParentUnit BusinessUnitResourceIdentifier `tfsdk:"parent_unit"`
+ Stores []sharedtypes.StoreKeyReference `tfsdk:"store"`
+ Addresses []sharedtypes.Address `tfsdk:"address"`
+}
+
+func (d *Division) draft() (platform.DivisionDraft, error) {
+ mode := platform.BusinessUnitStoreMode(d.StoreMode.ValueString())
+ associateMode := platform.BusinessUnitAssociateMode(d.AssociateMode.ValueString())
+ status := platform.BusinessUnitStatus(d.Status.ValueString())
+ approvalRuleMode := platform.BusinessUnitApprovalRuleMode(d.ApprovalRuleMode.ValueString())
+
+ var addresses []platform.BaseAddress
+ for _, a := range d.Addresses {
+ addresses = append(addresses, a.Draft())
+ }
+
+ var stores []platform.StoreResourceIdentifier
+ for _, s := range d.Stores {
+ stores = append(stores, platform.StoreResourceIdentifier{
+ Key: s.Key.ValueStringPointer(),
+ })
+ }
+
+ var shippingAddressIndexes []int
+ for _, key := range d.ShippingAddressKeys {
+ i := slices.IndexFunc(d.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == key.ValueString()
+ })
+
+ if i == -1 {
+ return platform.DivisionDraft{}, fmt.Errorf("shipping address key %s is not in addresses", key.ValueString())
+ }
+
+ shippingAddressIndexes = append(shippingAddressIndexes, i)
+ }
+
+ var billingAddressIndexes []int
+ for _, key := range d.BillingAddressKeys {
+ i := slices.IndexFunc(d.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == key.ValueString()
+ })
+
+ if i == -1 {
+ return platform.DivisionDraft{}, fmt.Errorf("billing address key %s is not in addresses", key.ValueString())
+ }
+
+ billingAddressIndexes = append(billingAddressIndexes, i)
+ }
+
+ var defaultBillingAddressIndex *int
+ if !d.DefaultBillingAddressKey.IsNull() {
+ i := slices.IndexFunc(d.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == d.DefaultBillingAddressKey.ValueString()
+ })
+
+ if i == -1 {
+ return platform.DivisionDraft{}, fmt.Errorf("default billing address key %s is not in addresses", d.DefaultBillingAddressKey.ValueString())
+ }
+
+ defaultBillingAddressIndex = &i
+ }
+
+ var defaultShippingAddressIndex *int
+ if !d.DefaultShippingAddressKey.IsNull() {
+ i := slices.IndexFunc(d.Addresses, func(a sharedtypes.Address) bool {
+ return a.Key.ValueString() == d.DefaultShippingAddressKey.ValueString()
+ })
+
+ if i == -1 {
+ return platform.DivisionDraft{}, fmt.Errorf("default shipping address key %s is not in addresses", d.DefaultShippingAddressKey.ValueString())
+ }
+
+ defaultShippingAddressIndex = &i
+ }
+
+ return platform.DivisionDraft{
+ Key: d.Key.ValueString(),
+ Status: &status,
+ StoreMode: &mode,
+ AssociateMode: &associateMode,
+ ApprovalRuleMode: &approvalRuleMode,
+ ParentUnit: platform.BusinessUnitResourceIdentifier{
+ ID: d.ParentUnit.ID.ValueStringPointer(),
+ Key: d.ParentUnit.Key.ValueStringPointer(),
+ },
+ Stores: stores,
+ Name: d.Name.ValueString(),
+ ContactEmail: d.ContactEmail.ValueStringPointer(),
+ Addresses: addresses,
+ ShippingAddresses: shippingAddressIndexes,
+ BillingAddresses: billingAddressIndexes,
+ DefaultShippingAddress: defaultShippingAddressIndex,
+ DefaultBillingAddress: defaultBillingAddressIndex,
+ }, nil
+}
+
+func (d *Division) updateActions(plan Division) (platform.BusinessUnitUpdate, error) {
+ result := platform.BusinessUnitUpdate{
+ Version: int(d.Version.ValueInt64()),
+ Actions: []platform.BusinessUnitUpdateAction{},
+ }
+
+ if !d.Name.Equal(plan.Name) {
+ result.Actions = append(result.Actions, platform.BusinessUnitChangeNameAction{
+ Name: plan.Name.ValueString(),
+ })
+ }
+
+ if !d.ContactEmail.Equal(plan.ContactEmail) {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetContactEmailAction{
+ ContactEmail: plan.ContactEmail.ValueStringPointer(),
+ })
+ }
+
+ if !d.Status.Equal(plan.Status) {
+ result.Actions = append(result.Actions, platform.BusinessUnitChangeStatusAction{
+ Status: plan.Status.ValueString(),
+ })
+ }
+
+ if !d.AssociateMode.Equal(plan.AssociateMode) {
+ result.Actions = append(result.Actions, platform.BusinessUnitChangeAssociateModeAction{
+ AssociateMode: platform.BusinessUnitAssociateMode(plan.AssociateMode.ValueString()),
+ })
+ }
+
+ if !d.ApprovalRuleMode.Equal(plan.ApprovalRuleMode) {
+ result.Actions = append(result.Actions, platform.BusinessUnitChangeApprovalRuleModeAction{
+ ApprovalRuleMode: platform.BusinessUnitApprovalRuleMode(plan.ApprovalRuleMode.ValueString()),
+ })
+ }
+
+ if !d.StoreMode.Equal(plan.StoreMode) {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetStoreModeAction{
+ StoreMode: platform.BusinessUnitStoreMode(plan.StoreMode.ValueString()),
+ Stores: []platform.StoreResourceIdentifier{},
+ })
+ }
+
+ if !reflect.DeepEqual(d.Stores, plan.Stores) {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetStoresAction{
+ Stores: pie.Map(plan.Stores, func(s sharedtypes.StoreKeyReference) platform.StoreResourceIdentifier {
+ return platform.StoreResourceIdentifier{
+ Key: s.Key.ValueStringPointer(),
+ }
+ }),
+ })
+ }
+
+ if !reflect.DeepEqual(d.Addresses, plan.Addresses) {
+ addressAddActions := sharedtypes.AddressesAddActions(d.Addresses, plan.Addresses)
+ for _, action := range addressAddActions {
+ result.Actions = append(result.Actions, action)
+ }
+
+ addressChangeActions := sharedtypes.AddressesChangeActions(d.Addresses, plan.Addresses)
+ for _, action := range addressChangeActions {
+ result.Actions = append(result.Actions, action)
+ }
+ }
+
+ if !d.DefaultShippingAddressKey.Equal(plan.DefaultShippingAddressKey) {
+ if plan.DefaultShippingAddressKey.IsNull() {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultShippingAddressAction{})
+ } else {
+ if !pie.Contains(plan.ShippingAddressKeys, plan.DefaultShippingAddressKey) {
+ return result, fmt.Errorf("default shipping address key %s is not in shipping address keys", plan.DefaultShippingAddressKey.ValueString())
+ }
+
+ result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultShippingAddressAction{
+ AddressKey: plan.DefaultShippingAddressKey.ValueStringPointer(),
+ })
+ }
+ }
+
+ if !d.DefaultBillingAddressKey.Equal(plan.DefaultBillingAddressKey) {
+ if plan.DefaultBillingAddressKey.IsNull() {
+ result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultBillingAddressAction{})
+ } else {
+ if !pie.Contains(plan.BillingAddressKeys, plan.DefaultBillingAddressKey) {
+ return result, fmt.Errorf("default billing address key %s is not in billing address keys", plan.DefaultBillingAddressKey.ValueString())
+ }
+
+ result.Actions = append(result.Actions, platform.BusinessUnitSetDefaultBillingAddressAction{
+ AddressKey: plan.DefaultBillingAddressKey.ValueStringPointer(),
+ })
+ }
+ }
+
+ if !reflect.DeepEqual(d.ShippingAddressKeys, plan.ShippingAddressKeys) {
+ // find shipping addresses to be added
+ for _, i := range plan.ShippingAddressKeys {
+ if !pie.Contains(d.ShippingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitAddShippingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+
+ // find shipping addresses to be removed
+ for _, i := range d.ShippingAddressKeys {
+ if !pie.Contains(plan.ShippingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitRemoveShippingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+ }
+
+ if !reflect.DeepEqual(d.BillingAddressKeys, plan.BillingAddressKeys) {
+ // find billing addresses to be added
+ for _, i := range plan.BillingAddressKeys {
+ if !pie.Contains(d.BillingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitAddBillingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+
+ // find billing addresses to be removed
+ for _, i := range d.BillingAddressKeys {
+ if !pie.Contains(plan.BillingAddressKeys, i) {
+ result.Actions = append(result.Actions, platform.BusinessUnitRemoveBillingAddressIdAction{
+ AddressKey: i.ValueStringPointer(),
+ })
+ }
+ }
+ }
+
+ // We need to delete addresses only after we have removed keys
+ if !reflect.DeepEqual(d.Addresses, plan.Addresses) {
+ addressDeleteActions := sharedtypes.AddressesDeleteActions(d.Addresses, plan.Addresses)
+ for _, action := range addressDeleteActions {
+ result.Actions = append(result.Actions, action)
+ }
+ }
+
+ return result, nil
+}
+
+// NewDivisionFromNative creates a new Division from a platform.Division.
+func NewDivisionFromNative(bu *platform.BusinessUnit) (Division, error) {
+ data := (*bu).(map[string]interface{})
+ var d platform.Division
+ err := utils.DecodeStruct(data, &d)
+ if err != nil {
+ return Division{}, err
+ }
+
+ parent := BusinessUnitResourceIdentifier{
+ Key: types.StringValue(d.ParentUnit.Key),
+ }
+
+ var defaultShippingAddressKey *string
+ if d.DefaultShippingAddressId != nil {
+ i := slices.IndexFunc(d.Addresses, func(a platform.Address) bool {
+ return *a.ID == *d.DefaultShippingAddressId
+ })
+ defaultShippingAddressKey = d.Addresses[i].Key
+ }
+ var defaultBillingAddressKey *string
+ if d.DefaultBillingAddressId != nil {
+ i := slices.IndexFunc(d.Addresses, func(a platform.Address) bool {
+ return *a.ID == *d.DefaultBillingAddressId
+ })
+ defaultBillingAddressKey = d.Addresses[i].Key
+ }
+
+ var shippingAddressKeys []types.String
+ for _, id := range d.ShippingAddressIds {
+ i := slices.IndexFunc(d.Addresses, func(a platform.Address) bool {
+ return *a.ID == id
+ })
+ shippingAddressKeys = append(shippingAddressKeys, types.StringPointerValue(d.Addresses[i].Key))
+ }
+
+ var billingAddressKeys []types.String
+ for _, id := range d.BillingAddressIds {
+ i := slices.IndexFunc(d.Addresses, func(a platform.Address) bool {
+ return *a.ID == id
+ })
+ billingAddressKeys = append(billingAddressKeys, types.StringPointerValue(d.Addresses[i].Key))
+ }
+
+ var stores []sharedtypes.StoreKeyReference
+ for _, s := range d.Stores {
+ stores = append(stores, sharedtypes.NewStoreKeyReferenceFromNative(&s))
+ }
+
+ var addresses []sharedtypes.Address
+ for _, a := range d.Addresses {
+ addresses = append(addresses, sharedtypes.NewAddressFromNative(&a))
+ }
+
+ division := Division{
+ ID: types.StringValue(d.ID),
+ Version: types.Int64Value(int64(d.Version)),
+ Key: types.StringValue(d.Key),
+ Status: types.StringValue(string(d.Status)),
+ ParentUnit: parent,
+ StoreMode: types.StringValue(string(d.StoreMode)),
+ ApprovalRuleMode: types.StringValue(string(d.ApprovalRuleMode)),
+ Name: types.StringValue(d.Name),
+ ContactEmail: types.StringPointerValue(d.ContactEmail),
+ DefaultShippingAddressKey: types.StringPointerValue(defaultShippingAddressKey),
+ DefaultBillingAddressKey: types.StringPointerValue(defaultBillingAddressKey),
+ AssociateMode: types.StringValue(string(d.AssociateMode)),
+ Stores: stores,
+ Addresses: addresses,
+ ShippingAddressKeys: shippingAddressKeys,
+ BillingAddressKeys: billingAddressKeys,
+ }
+
+ sort.Slice(division.Addresses, func(i, j int) bool {
+ return division.Addresses[i].Key.ValueString() < division.Addresses[j].Key.ValueString()
+ })
+
+ sort.Slice(division.ShippingAddressKeys, func(i, j int) bool {
+ return division.ShippingAddressKeys[i].ValueString() < division.ShippingAddressKeys[j].ValueString()
+ })
+
+ sort.Slice(division.BillingAddressKeys, func(i, j int) bool {
+ return division.BillingAddressKeys[i].ValueString() < division.BillingAddressKeys[j].ValueString()
+ })
+
+ return division, nil
+}
+
+// BusinessUnitResourceIdentifier is a resource identifier for a business unit.
+type BusinessUnitResourceIdentifier struct {
+ ID types.String `tfsdk:"id"`
+ Key types.String `tfsdk:"key"`
+}
diff --git a/internal/resources/business_unit_division/model_test.go b/internal/resources/business_unit_division/model_test.go
new file mode 100644
index 00000000..d76bd32a
--- /dev/null
+++ b/internal/resources/business_unit_division/model_test.go
@@ -0,0 +1,446 @@
+package business_unit_division
+
+import (
+ "github.com/labd/terraform-provider-commercetools/internal/sharedtypes"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/labd/commercetools-go-sdk/platform"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBusinessUnit_Division_Draft(t *testing.T) {
+ cases := []struct {
+ name string
+ division Division
+ expected platform.DivisionDraft
+ }{
+ {
+ name: "Basic division draft",
+ division: Division{
+ Key: types.StringValue("division-key"),
+ Status: types.StringValue("Active"),
+ Name: types.StringValue("division Name"),
+ ContactEmail: types.StringValue("contact@example.com"),
+ StoreMode: types.StringValue(string(platform.BusinessUnitStoreModeExplicit)),
+ AssociateMode: types.StringValue(string(platform.BusinessUnitAssociateModeExplicit)),
+ ApprovalRuleMode: types.StringValue(string(platform.BusinessUnitApprovalRuleModeExplicit)),
+ ParentUnit: BusinessUnitResourceIdentifier{
+ Key: types.StringValue("parent-key"),
+ },
+ Addresses: []sharedtypes.Address{
+ {
+ Key: types.StringValue("address-1"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("New York"),
+ },
+ {
+ Key: types.StringValue("address-2"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("Detroit"),
+ },
+ },
+ Stores: []sharedtypes.StoreKeyReference{
+ {Key: types.StringValue("store-1")},
+ },
+ ShippingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ BillingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ DefaultBillingAddressKey: types.StringValue("address-2"),
+ DefaultShippingAddressKey: types.StringValue("address-2"),
+ },
+ expected: platform.DivisionDraft{
+ Key: "division-key",
+ Status: utils.Ref(platform.BusinessUnitStatusActive),
+ Name: "division Name",
+ StoreMode: utils.Ref(platform.BusinessUnitStoreModeExplicit),
+ AssociateMode: utils.Ref(platform.BusinessUnitAssociateModeExplicit),
+ ApprovalRuleMode: utils.Ref(platform.BusinessUnitApprovalRuleModeExplicit),
+ ContactEmail: utils.Ref("contact@example.com"),
+ ParentUnit: platform.BusinessUnitResourceIdentifier{
+ Key: utils.Ref("parent-key"),
+ },
+ Addresses: []platform.BaseAddress{
+ {
+ Key: utils.Ref("address-1"),
+ Country: "US",
+ City: utils.Ref("New York"),
+ },
+ {
+ Key: utils.Ref("address-2"),
+ Country: "US",
+ City: utils.Ref("Detroit"),
+ },
+ },
+ Stores: []platform.StoreResourceIdentifier{
+ {
+ Key: utils.Ref("store-1"),
+ },
+ },
+ DefaultShippingAddress: utils.Ref(1),
+ DefaultBillingAddress: utils.Ref(1),
+ ShippingAddresses: []int{0, 1},
+ BillingAddresses: []int{0, 1},
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ result, err := c.division.draft()
+ assert.NoError(t, err)
+ assert.Equal(t, c.expected, result)
+ })
+ }
+}
+
+func TestBusinessUnit_Division_UpdateActions(t *testing.T) {
+ cases := []struct {
+ name string
+ state Division
+ plan Division
+ expected platform.BusinessUnitUpdate
+ }{
+ {
+ "business unit update name",
+ Division{
+ Name: types.StringValue("Example business unit"),
+ },
+ Division{
+ Name: types.StringValue("Updated business unit"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitChangeNameAction{
+ Name: "Updated business unit",
+ },
+ },
+ },
+ },
+ {
+ "business unit update contact email",
+ Division{
+ ContactEmail: types.StringValue("info@example.com"),
+ },
+ Division{
+ ContactEmail: types.StringValue("new@example.com"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetContactEmailAction{
+ ContactEmail: types.StringValue("new@example.com").ValueStringPointer(),
+ },
+ },
+ },
+ },
+ {
+ "business unit update status",
+ Division{
+ Status: types.StringValue("Active"),
+ },
+ Division{
+ Status: types.StringValue("Inactive"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitChangeStatusAction{
+ Status: "Inactive",
+ },
+ },
+ },
+ },
+ {
+ "business unit update default shipping address",
+ Division{
+ ShippingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultShippingAddressKey: types.StringValue("some-random-id"),
+ },
+ Division{
+ ShippingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultShippingAddressKey: types.StringValue("another-random-id"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetDefaultShippingAddressAction{
+ AddressKey: types.StringValue("another-random-id").ValueStringPointer(),
+ },
+ },
+ },
+ },
+ {
+ "business unit update default billing address",
+ Division{
+ BillingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultBillingAddressKey: types.StringValue("some-random-id"),
+ },
+ Division{
+ BillingAddressKeys: []types.String{types.StringValue("some-random-id"), types.StringValue("another-random-id")},
+ DefaultBillingAddressKey: types.StringValue("another-random-id"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetDefaultBillingAddressAction{
+ AddressKey: types.StringValue("another-random-id").ValueStringPointer(),
+ },
+ },
+ },
+ },
+ {
+ "business unit update associate mode",
+ Division{
+ AssociateMode: types.StringValue("Explicit"),
+ },
+ Division{
+ AssociateMode: types.StringValue("ExplicitAndFromParent"),
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitChangeAssociateModeAction{
+ AssociateMode: "ExplicitAndFromParent",
+ },
+ },
+ },
+ },
+ {
+ "business unit update stores",
+ Division{
+ Stores: []sharedtypes.StoreKeyReference{
+ {
+ Key: types.StringValue("store-1"),
+ },
+ {
+ Key: types.StringValue("store-2"),
+ },
+ },
+ },
+ Division{
+ Stores: []sharedtypes.StoreKeyReference{
+ {
+ Key: types.StringValue("store-1"),
+ },
+ {
+ Key: types.StringValue("store-3"),
+ },
+ },
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitSetStoresAction{
+ Stores: []platform.StoreResourceIdentifier{
+ {
+ Key: types.StringValue("store-1").ValueStringPointer(),
+ ID: nil,
+ },
+ {
+
+ Key: types.StringValue("store-3").ValueStringPointer(),
+ ID: nil,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "business unit add address",
+ Division{
+ Addresses: []sharedtypes.Address{},
+ },
+ Division{
+ Addresses: []sharedtypes.Address{
+ {
+ Key: types.StringValue("new-york-office"),
+ Country: types.StringValue("US"),
+ Salutation: types.StringValue("Mr."),
+ FirstName: types.StringValue("John"),
+ LastName: types.StringValue("Doe"),
+ StreetName: types.StringValue("Main St."),
+ StreetNumber: types.StringValue("123"),
+ AdditionalStreetInfo: types.StringValue("Apt. 1"),
+ PostalCode: types.StringValue("12345"),
+ City: types.StringValue("New York"),
+ Region: types.StringValue("New York"),
+ State: types.StringValue("New York"),
+ Company: types.StringValue("Example Inc."),
+ Department: types.StringValue("Sales"),
+ Building: types.StringValue("1"),
+ Apartment: types.StringValue("1"),
+ POBox: types.StringValue("123"),
+ Phone: types.StringValue("1234567890"),
+ Mobile: types.StringValue("1234567890"),
+ Fax: types.StringValue("1234567890"),
+ },
+ },
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitAddAddressAction{
+ Address: platform.BaseAddress{
+ Key: utils.StringRef("new-york-office"),
+ Country: "US",
+ Salutation: utils.StringRef("Mr."),
+ FirstName: utils.StringRef("John"),
+ LastName: utils.StringRef("Doe"),
+ StreetName: utils.StringRef("Main St."),
+ StreetNumber: utils.StringRef("123"),
+ AdditionalStreetInfo: utils.StringRef("Apt. 1"),
+ PostalCode: utils.StringRef("12345"),
+ City: utils.StringRef("New York"),
+ Region: utils.StringRef("New York"),
+ State: utils.StringRef("New York"),
+ Company: utils.StringRef("Example Inc."),
+ Department: utils.StringRef("Sales"),
+ Building: utils.StringRef("1"),
+ Apartment: utils.StringRef("1"),
+ POBox: utils.StringRef("123"),
+ Phone: utils.StringRef("1234567890"),
+ Mobile: utils.StringRef("1234567890"),
+ Fax: utils.StringRef("1234567890"),
+ },
+ },
+ },
+ },
+ },
+ {
+ "business unit remove address",
+ Division{
+ Addresses: []sharedtypes.Address{
+ {
+ Key: types.StringValue("new-york-office"),
+ Country: types.StringValue("US"),
+ Salutation: types.StringValue("Mr."),
+ FirstName: types.StringValue("John"),
+ LastName: types.StringValue("Doe"),
+ StreetName: types.StringValue("Main St."),
+ StreetNumber: types.StringValue("123"),
+ AdditionalStreetInfo: types.StringValue("Apt. 1"),
+ PostalCode: types.StringValue("12345"),
+ City: types.StringValue("New York"),
+ Region: types.StringValue("New York"),
+ State: types.StringValue("New York"),
+ Company: types.StringValue("Example Inc."),
+ Department: types.StringValue("Sales"),
+ Building: types.StringValue("1"),
+ Apartment: types.StringValue("1"),
+ POBox: types.StringValue("123"),
+ Phone: types.StringValue("1234567890"),
+ Mobile: types.StringValue("1234567890"),
+ Fax: types.StringValue("1234567890"),
+ },
+ },
+ },
+ Division{
+ Addresses: []sharedtypes.Address{},
+ },
+ platform.BusinessUnitUpdate{
+ Actions: []platform.BusinessUnitUpdateAction{
+ platform.BusinessUnitRemoveAddressAction{
+ AddressKey: utils.StringRef("new-york-office"),
+ },
+ },
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ result, err := c.state.updateActions(c.plan)
+ assert.NoError(t, err)
+ assert.EqualValues(t, c.expected, result)
+ })
+ }
+}
+
+func TestBusinessUnit_Division_NewDivisionFromNative(t *testing.T) {
+ cases := []struct {
+ name string
+ division map[string]interface{}
+ expected Division
+ }{
+ {
+ name: "Basic division draft",
+ division: map[string]interface{}{
+ "id": "division-id",
+ "key": "division-key",
+ "version": 1,
+ "status": platform.BusinessUnitStatusActive,
+ "approvalRuleMode": platform.BusinessUnitApprovalRuleModeExplicit,
+ "associateMode": platform.BusinessUnitAssociateModeExplicit,
+ "storeMode": platform.BusinessUnitStoreModeExplicit,
+ "name": "division Name",
+ "contactEmail": utils.Ref("contact@example.com"),
+ "addresses": []map[string]interface{}{
+ {
+ "id": utils.Ref("address-id-1"),
+ "key": utils.Ref("address-1"),
+ "country": "US",
+ "city": utils.Ref("New York"),
+ },
+ {
+ "id": utils.Ref("address-id-2"),
+ "key": utils.Ref("address-2"),
+ "country": "US",
+ "city": utils.Ref("Detroit"),
+ },
+ },
+ "stores": []map[string]interface{}{
+ {"key": "store-1"},
+ },
+ "parentUnit": map[string]interface{}{
+ "key": "parent-key",
+ },
+ "shippingAddressIds": []string{"address-id-1", "address-id-2"},
+ "billingAddressIds": []string{"address-id-1", "address-id-2"},
+ "defaultBillingAddressId": utils.Ref("address-id-2"),
+ "defaultShippingAddressId": utils.Ref("address-id-2"),
+ },
+ expected: Division{
+ ID: types.StringValue("division-id"),
+ Key: types.StringValue("division-key"),
+ Version: types.Int64Value(1),
+ Status: types.StringValue(string(platform.BusinessUnitStatusActive)),
+ Name: types.StringValue("division Name"),
+ ContactEmail: types.StringValue("contact@example.com"),
+ StoreMode: types.StringValue(string(platform.BusinessUnitStoreModeExplicit)),
+ AssociateMode: types.StringValue(string(platform.BusinessUnitAssociateModeExplicit)),
+ ApprovalRuleMode: types.StringValue(string(platform.BusinessUnitApprovalRuleModeExplicit)),
+ ParentUnit: BusinessUnitResourceIdentifier{
+ Key: types.StringValue("parent-key"),
+ },
+ Addresses: []sharedtypes.Address{
+ {
+ ID: types.StringValue("address-id-1"),
+ Key: types.StringValue("address-1"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("New York"),
+ },
+ {
+ ID: types.StringValue("address-id-2"),
+ Key: types.StringValue("address-2"),
+ Country: types.StringValue("US"),
+ City: types.StringValue("Detroit"),
+ },
+ },
+ Stores: []sharedtypes.StoreKeyReference{
+ {Key: types.StringValue("store-1")},
+ },
+ ShippingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ BillingAddressKeys: []types.String{types.StringValue("address-1"), types.StringValue("address-2")},
+ DefaultBillingAddressKey: types.StringValue("address-2"),
+ DefaultShippingAddressKey: types.StringValue("address-2"),
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ var data platform.BusinessUnit
+ err := utils.DecodeStruct(c.division, &data)
+ assert.NoError(t, err)
+
+ result, err := NewDivisionFromNative(&data)
+ assert.NoError(t, err)
+ assert.Equal(t, c.expected, result)
+ })
+ }
+}
diff --git a/internal/resources/business_unit_division/resource.go b/internal/resources/business_unit_division/resource.go
new file mode 100644
index 00000000..50e2a5d6
--- /dev/null
+++ b/internal/resources/business_unit_division/resource.go
@@ -0,0 +1,369 @@
+package business_unit_division
+
+import (
+ "context"
+ "errors"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
+ "github.com/labd/terraform-provider-commercetools/internal/sharedtypes"
+ "regexp"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
+ "github.com/labd/commercetools-go-sdk/platform"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+)
+
+var (
+ _ resource.Resource = &divisionResource{}
+ _ resource.ResourceWithConfigure = &divisionResource{}
+ _ resource.ResourceWithImportState = &divisionResource{}
+)
+
+type divisionResource struct {
+ client *platform.ByProjectKeyRequestBuilder
+}
+
+// NewDivisionResource creates a new resource for the Division type.
+func NewDivisionResource() resource.Resource {
+ return &divisionResource{}
+}
+
+// Schema implements resource.Resource.
+func (b *divisionResource) Schema(_ context.Context, req resource.SchemaRequest, res *resource.SchemaResponse) {
+ res.Schema = schema.Schema{
+ MarkdownDescription: "Business Unit type to represent the top level of a business. Contains specific fields and values that differentiate a Division from the generic BusinessUnit.\n\n" +
+ "See also the [Business Unit API Documentation](https://docs.commercetools.com/api/projects/business-units",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "Unique identifier of the Division.",
+ Computed: true,
+ },
+ "version": schema.Int64Attribute{
+ MarkdownDescription: "The current version of the Division.",
+ Computed: true,
+ },
+ "key": schema.StringAttribute{
+ MarkdownDescription: "User-defined unique identifier for the Division.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.LengthBetween(2, 256),
+ stringvalidator.RegexMatches(
+ regexp.MustCompile("^[A-Za-z0-9_-]+$"),
+ "Key must match pattern ^[A-Za-z0-9_-]+$",
+ ),
+ },
+ },
+ "status": schema.StringAttribute{
+ MarkdownDescription: "Indicates whether the Business Unit can be edited and used in [Orders](https://docs.commercetools.com/api/projects/orders). Defaults to `Active`.",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(string(platform.BusinessUnitStatusActive)),
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ string(platform.BusinessUnitStatusActive),
+ string(platform.BusinessUnitStatusInactive),
+ ),
+ },
+ },
+ "store_mode": schema.StringAttribute{
+ MarkdownDescription: "Defines whether the Stores of the Business Unit are set directly on the Business Unit or are inherited from a parent. Defaults to `FromParent`",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(string(platform.BusinessUnitStoreModeFromParent)),
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ string(platform.BusinessUnitStoreModeFromParent),
+ string(platform.BusinessUnitStoreModeExplicit),
+ ),
+ },
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "The name of the Division.",
+ Required: true,
+ },
+ "contact_email": schema.StringAttribute{
+ MarkdownDescription: "The email address of the Division.",
+ Optional: true,
+ },
+ "associate_mode": schema.StringAttribute{
+ MarkdownDescription: "Determines whether the Business Unit can inherit Associates from a parent. Defaults to `ExplicitAndFromParent`.",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(string(platform.BusinessUnitAssociateModeExplicitAndFromParent)),
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ string(platform.BusinessUnitAssociateModeExplicitAndFromParent),
+ string(platform.BusinessUnitAssociateModeExplicit),
+ ),
+ },
+ },
+ "approval_rule_mode": schema.StringAttribute{
+ MarkdownDescription: "Determines whether the Business Unit can inherit Approval Rules from a parent. Defaults to `ExplicitAndFromParent`.",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(string(platform.BusinessUnitApprovalRuleModeExplicitAndFromParent)),
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ string(platform.BusinessUnitApprovalRuleModeExplicit),
+ string(platform.BusinessUnitApprovalRuleModeExplicitAndFromParent),
+ ),
+ },
+ },
+ "shipping_address_keys": schema.ListAttribute{
+ MarkdownDescription: "List of the shipping addresses used by the Division.",
+ Optional: true,
+ ElementType: types.StringType,
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "default_shipping_address_key": schema.StringAttribute{
+ MarkdownDescription: "Key of the default shipping Address.",
+ Optional: true,
+ },
+ "billing_address_keys": schema.ListAttribute{
+ MarkdownDescription: "List of the billing addresses used by the Division.",
+ Optional: true,
+ ElementType: types.StringType,
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "default_billing_address_key": schema.StringAttribute{
+ MarkdownDescription: "Key of the default billing Address.",
+ Optional: true,
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "store": sharedtypes.StoreKeyReferenceBlockSchema,
+ "address": sharedtypes.AddressBlockSchema,
+ "parent_unit": schema.SingleNestedBlock{
+ MarkdownDescription: "Reference to a parent Business Unit by its key.",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "User-defined unique identifier of the Business Unit",
+ Optional: true,
+ },
+ "key": schema.StringAttribute{
+ MarkdownDescription: "User-defined unique key of the Business Unit",
+ Optional: true,
+ },
+ },
+ },
+ },
+ }
+}
+
+// Metadata implements resource.Resource.
+func (b *divisionResource) Metadata(_ context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) {
+ res.TypeName = req.ProviderTypeName + "_business_unit_division"
+}
+
+// ImportState implements resource.ResourceWithImportState.
+func (b *divisionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, res *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, res)
+}
+
+// Configure implements resource.ResourceWithConfigure.
+func (b *divisionResource) Configure(ctx context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ data, ok := req.ProviderData.(*utils.ProviderData)
+ if !ok {
+ return
+ }
+
+ b.client = data.Client
+}
+
+// Create implements resource.Resource.
+func (b *divisionResource) Create(ctx context.Context, req resource.CreateRequest, res *resource.CreateResponse) {
+ var plan Division
+ diags := req.Plan.Get(ctx, &plan)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ draft, err := plan.draft()
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error creating business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+
+ }
+
+ var bu *platform.BusinessUnit
+ err = retry.RetryContext(ctx, 20*time.Second, func() *retry.RetryError {
+ var err error
+ bu, err = b.client.BusinessUnits().Post(draft).Execute(ctx)
+
+ return utils.ProcessRemoteError(err)
+ })
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error creating business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ current, err := NewDivisionFromNative(bu)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error mapping business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ diags = res.State.Set(ctx, current)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Delete implements resource.Resource.
+func (b *divisionResource) Delete(ctx context.Context, req resource.DeleteRequest, res *resource.DeleteResponse) {
+ var state Division
+
+ diags := req.State.Get(ctx, &state)
+ res.Diagnostics.Append(diags...)
+
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ err := retry.RetryContext(
+ ctx,
+ 5*time.Second,
+ func() *retry.RetryError {
+ _, err := b.client.BusinessUnits().
+ WithId(state.ID.ValueString()).
+ Delete().
+ Version(int(state.Version.ValueInt64())).
+ Execute(ctx)
+
+ return utils.ProcessRemoteError(err)
+ },
+ )
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error deleting business unit",
+ "Could not delete business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+}
+
+// Read implements resource.Resource.
+func (b *divisionResource) Read(ctx context.Context, req resource.ReadRequest, res *resource.ReadResponse) {
+ var state Division
+ diags := req.State.Get(ctx, &state)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ bu, err := b.client.BusinessUnits().WithId(state.ID.ValueString()).Get().Execute(ctx)
+ if err != nil {
+ if errors.Is(err, platform.ErrNotFound) {
+ res.State.RemoveResource(ctx)
+ return
+ }
+
+ res.Diagnostics.AddError(
+ "Error reading business unit",
+ "Could not retrieve the business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ current, err := NewDivisionFromNative(bu)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error mapping business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ diags = res.State.Set(ctx, current)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Update implements resource.Resource.
+func (b *divisionResource) Update(ctx context.Context, req resource.UpdateRequest, res *resource.UpdateResponse) {
+ var plan Division
+ diags := req.Plan.Get(ctx, &plan)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ var state Division
+ diags = req.State.Get(ctx, &state)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+
+ input, err := state.updateActions(plan)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error updating business unit",
+ "Could not update business unit, unexpected error: "+err.Error(),
+ )
+ return
+
+ }
+ var bu *platform.BusinessUnit
+
+ err = retry.RetryContext(ctx, 5*time.Second, func() *retry.RetryError {
+ var err error
+ bu, err = b.client.BusinessUnits().
+ WithId(state.ID.ValueString()).
+ Post(input).
+ Execute(ctx)
+
+ return utils.ProcessRemoteError(err)
+ })
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error updating business unit",
+ "Could not update business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ current, err := NewDivisionFromNative(bu)
+ if err != nil {
+ res.Diagnostics.AddError(
+ "Error mapping business unit",
+ "Could not create business unit, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ diags = res.State.Set(ctx, ¤t)
+ res.Diagnostics.Append(diags...)
+ if res.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/internal/resources/business_unit_division/resource_test.go b/internal/resources/business_unit_division/resource_test.go
new file mode 100644
index 00000000..907a871a
--- /dev/null
+++ b/internal/resources/business_unit_division/resource_test.go
@@ -0,0 +1,132 @@
+package business_unit_division_test
+
+import (
+ "context"
+ "github.com/labd/terraform-provider-commercetools/internal/resources/business_unit_division"
+ "testing"
+
+ fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+
+ "github.com/labd/terraform-provider-commercetools/internal/acctest"
+ "github.com/labd/terraform-provider-commercetools/internal/utils"
+)
+
+func TestDivisionSchemaImplementation(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ schemaRequest := fwresource.SchemaRequest{}
+ schemaResponse := &fwresource.SchemaResponse{}
+
+ business_unit_division.NewDivisionResource().Schema(ctx, schemaRequest, schemaResponse)
+
+ if schemaResponse.Diagnostics.HasError() {
+ t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
+ }
+
+ // schema validation
+ diagnostics := schemaResponse.Schema.ValidateImplementation(ctx)
+
+ if diagnostics.HasError() {
+ t.Fatalf("Schema validation diagnostics: %+v", diagnostics)
+ }
+}
+
+func TestBusinessUnitResource_Division(t *testing.T) {
+ r := "commercetools_business_unit_division.acme_division"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { acctest.TestAccPreCheck(t) },
+ ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
+ CheckDestroy: testBusinessUnitDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: businessUnitTFResourceDef("", "", ""),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr(r, "key", "acme-division"),
+ resource.TestCheckResourceAttr(r, "name", "Acme Company Business Unit"),
+ resource.TestCheckResourceAttr(r, "status", "Active"),
+ resource.TestCheckResourceAttr(r, "stores.#", "2"),
+ resource.TestCheckResourceAttr(r, "stores.0.key", "acme-usa"),
+ resource.TestCheckResourceAttr(r, "stores.1.key", "acme-germany"),
+ resource.TestCheckResourceAttr(r, "addresses.#", "1"),
+ ),
+ },
+ {
+ Config: businessUnitTFResourceDef("Acme Business Unit - Updated", "Inactive", ""),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr(r, "key", "acme-division"),
+ resource.TestCheckResourceAttr(r, "status", "Inactive"),
+ resource.TestCheckResourceAttr(r, "stores.#", "2"),
+ resource.TestCheckResourceAttr(r, "stores.0.key", "acme-usa"),
+ resource.TestCheckResourceAttr(r, "stores.1.key", "acme-germany"),
+ resource.TestCheckResourceAttr(r, "addresses.#", "1"),
+ ),
+ },
+ },
+ })
+}
+
+func testBusinessUnitDestroy(_ *terraform.State) error {
+ return nil
+}
+
+func businessUnitTFResourceDef(name, status, email string) string {
+ if status == "" {
+ status = "Active"
+ }
+
+ if email == "" {
+ email = "acme@example.com"
+ }
+
+ if name == "" {
+ name = "Acme Company Business Unit"
+ }
+
+ return utils.HCLTemplate(`resource "commercetools_business_unit_division" "acme_division" {
+ key = "acme-division"
+ name = {{ .name }}
+ status = {{ .status }}
+ contact_email = {{ .email}}
+
+ store {
+ key = "acme-usa"
+ }
+
+ store {
+ key = "acme-germany"
+ }
+
+ address {
+ key = "acme-business-unit-address"
+ title = "Acme Business Unit Address"
+ salutation = "Mr."
+ first_name = "John"
+ last_name = "Doe"
+ street_name = "Main Street"
+ street_number = "1"
+ additional_street_info = "Additional Street Info"
+ postal_code = "12345"
+ city = "Berlin"
+ region = "Berlin"
+ country = "DE"
+ company = "Acme"
+ department = "IT"
+ building = "Building"
+ apartment = "Apartment"
+ po_box = "P.O. Box"
+ phone = "123456789"
+ mobile = "987654321"
+ }
+ default_shipping_address_id = "acme-business-unit-address"
+ default_billing_address_id = "acme-business-unit-address"
+}`, map[string]any{
+ "name": name,
+ "status": status,
+ "email": email,
+ })
+}
diff --git a/internal/resources/product_selection/resource.go b/internal/resources/product_selection/resource.go
index 82fa7599..df51478a 100644
--- a/internal/resources/product_selection/resource.go
+++ b/internal/resources/product_selection/resource.go
@@ -2,6 +2,7 @@ package product_selection
import (
"context"
+ "regexp"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
@@ -51,6 +52,13 @@ func (*productSelectionResource) Schema(_ context.Context, req resource.SchemaRe
"key": schema.StringAttribute{
Description: "User-defined unique identifier of the ProductSelection.",
Optional: true,
+ Validators: []validator.String{
+ stringvalidator.LengthBetween(2, 256),
+ stringvalidator.RegexMatches(
+ regexp.MustCompile("^[A-Za-z0-9_-]+$"),
+ "Key must match pattern ^[A-Za-z0-9_-]+$",
+ ),
+ },
},
"mode": schema.StringAttribute{
Description: "Specifies in which way the Products are assigned to the ProductSelection." +
diff --git a/internal/resources/product_selection/resource_test.go b/internal/resources/product_selection/resource_test.go
index 60c550c2..204f442c 100644
--- a/internal/resources/product_selection/resource_test.go
+++ b/internal/resources/product_selection/resource_test.go
@@ -12,7 +12,7 @@ import (
"github.com/labd/terraform-provider-commercetools/internal/utils"
)
-func TestProductSelctionResource_Create(t *testing.T) {
+func TestProductSelectionResource_Create(t *testing.T) {
rn := "commercetools_product_selection.test_product_selection"
id := "test_product_selection"
diff --git a/internal/resources/project/model.go b/internal/resources/project/model.go
index 9b299ca1..75d9fa8f 100644
--- a/internal/resources/project/model.go
+++ b/internal/resources/project/model.go
@@ -1,6 +1,7 @@
package project
import (
+ "fmt"
"reflect"
"github.com/elliotchance/pie/v2"
@@ -37,6 +38,8 @@ type Project struct {
ShippingRateInputType types.String `tfsdk:"shipping_rate_input_type"`
ShippingRateCartClassificationValue []models.CustomFieldLocalizedEnumValue `tfsdk:"shipping_rate_cart_classification_value"`
+
+ BusinessUnits []BusinessUnits `tfsdk:"business_units"`
}
func NewProjectFromNative(n *platform.Project) Project {
@@ -66,6 +69,7 @@ func NewProjectFromNative(n *platform.Project) Project {
},
},
ExternalOAuth: []ExternalOAuth{},
+ BusinessUnits: []BusinessUnits{},
}
// always set it to an empty list to avoid the wrong comparison in the update actions part
@@ -112,6 +116,18 @@ func NewProjectFromNative(n *platform.Project) Project {
}
}
+ if n.BusinessUnits != nil {
+ var businessUnits = BusinessUnits{
+ MyBusinessUnitStatusOnCreation: types.StringValue(string(n.BusinessUnits.MyBusinessUnitStatusOnCreation)),
+ }
+
+ if n.BusinessUnits.MyBusinessUnitAssociateRoleOnCreation != nil {
+ businessUnits.MyBusinessUnitAssociateRoleKeyOnCreation = types.StringValue(n.BusinessUnits.MyBusinessUnitAssociateRoleOnCreation.Key)
+ }
+
+ res.BusinessUnits = []BusinessUnits{businessUnits}
+ }
+
return res
}
@@ -132,22 +148,22 @@ func (p *Project) setStateData(o Project) {
p.Messages[0].DeleteDaysAfterCreation = o.Messages[0].DeleteDaysAfterCreation
}
}
- // If the state has no data for messages (0 items) and the configuration is
- // the default we match the state
+ // If the state has no data for messages (0 items) and the configuration is the default we match the state
if len(p.Messages) > 0 && p.Messages[0].isDefault() && (len(o.Messages) == 0 || o.Messages[0].isDefault()) {
p.Messages = o.Messages
}
+
+ if len(o.BusinessUnits) == 0 {
+ p.BusinessUnits = nil
+ }
}
-func (p *Project) updateActions(plan Project) platform.ProjectUpdate {
+func (p *Project) updateActions(plan Project) (platform.ProjectUpdate, error) {
result := platform.ProjectUpdate{
Version: int(p.Version.ValueInt64()),
Actions: []platform.ProjectUpdateAction{},
}
- // changeMyBusinessUnitStatusOnCreation
- // TODO
-
// changeCartsConfiguration
if !reflect.DeepEqual(p.Carts, plan.Carts) {
if len(plan.Carts) == 0 {
@@ -259,9 +275,6 @@ func (p *Project) updateActions(plan Project) platform.ProjectUpdate {
)
}
- // changeShoppingListsConfiguration
- // TODO
-
// setExternalOAuth
if !reflect.DeepEqual(p.ExternalOAuth, plan.ExternalOAuth) {
var value *platform.ExternalOAuth
@@ -301,7 +314,60 @@ func (p *Project) updateActions(plan Project) platform.ProjectUpdate {
)
}
- return result
+ // changeBusinessUnitConfiguration
+ if !reflect.DeepEqual(p.BusinessUnits, plan.BusinessUnits) {
+ // If the existing business unit configuration is nil, but the plan has a configuration we apply the configuration
+ if len(p.BusinessUnits) == 0 && len(plan.BusinessUnits) != 0 {
+ result.Actions = append(result.Actions,
+ platform.ProjectChangeBusinessUnitStatusOnCreationAction{
+ Status: platform.BusinessUnitConfigurationStatus(plan.BusinessUnits[0].MyBusinessUnitStatusOnCreation.ValueString()),
+ },
+ )
+
+ //TODO: should set associate role to nil, but that is not currently supported in the SDK
+ if plan.BusinessUnits[0].MyBusinessUnitAssociateRoleKeyOnCreation.ValueStringPointer() != nil {
+ result.Actions = append(result.Actions,
+ platform.ProjectSetBusinessUnitAssociateRoleOnCreationAction{
+ AssociateRole: platform.AssociateRoleResourceIdentifier{
+ Key: plan.BusinessUnits[0].MyBusinessUnitAssociateRoleKeyOnCreation.ValueStringPointer(),
+ },
+ },
+ )
+ }
+ } else if len(p.BusinessUnits) != 0 && len(plan.BusinessUnits) == 0 {
+ // If the existing business unit configuration is not nil, but the plan has no configuration we remove the configuration
+ result.Actions = append(result.Actions,
+ platform.ProjectChangeBusinessUnitStatusOnCreationAction{
+ Status: platform.BusinessUnitConfigurationStatusInactive,
+ },
+ )
+ //TODO: should set associate role to nil, but that is not currently supported in the SDK
+ } else {
+ if !p.BusinessUnits[0].MyBusinessUnitStatusOnCreation.Equal(plan.BusinessUnits[0].MyBusinessUnitStatusOnCreation) {
+ result.Actions = append(result.Actions,
+ platform.ProjectChangeBusinessUnitStatusOnCreationAction{
+ Status: platform.BusinessUnitConfigurationStatus(plan.BusinessUnits[0].MyBusinessUnitStatusOnCreation.ValueString()),
+ },
+ )
+ }
+ if !p.BusinessUnits[0].MyBusinessUnitAssociateRoleKeyOnCreation.Equal(plan.BusinessUnits[0].MyBusinessUnitAssociateRoleKeyOnCreation) {
+ if plan.BusinessUnits[0].MyBusinessUnitAssociateRoleKeyOnCreation.IsNull() {
+ return result, fmt.Errorf("AssociateRoleKeyReference cannot be set to nil after it has been assigned")
+ }
+
+ result.Actions = append(result.Actions,
+ platform.ProjectSetBusinessUnitAssociateRoleOnCreationAction{
+ AssociateRole: platform.AssociateRoleResourceIdentifier{
+ Key: plan.BusinessUnits[0].MyBusinessUnitAssociateRoleKeyOnCreation.ValueStringPointer(),
+ },
+ },
+ )
+ }
+ }
+
+ }
+
+ return result, nil
}
type Messages struct {
@@ -353,3 +419,37 @@ func (c Carts) toNative() platform.CartsConfiguration {
CountryTaxRateFallbackEnabled: utils.BoolRef(c.CountryTaxRateFallbackEnabled.ValueBool()),
}
}
+
+type BusinessUnits struct {
+ MyBusinessUnitStatusOnCreation types.String `tfsdk:"my_business_unit_status_on_creation"`
+ MyBusinessUnitAssociateRoleKeyOnCreation types.String `tfsdk:"my_business_unit_associate_role_key_on_creation"`
+}
+
+func (b BusinessUnits) toNative() platform.BusinessUnitDraft {
+ draft := platform.BusinessUnitConfiguration{
+ MyBusinessUnitStatusOnCreation: platform.BusinessUnitConfigurationStatus(b.MyBusinessUnitStatusOnCreation.ValueString()),
+ }
+
+ if !b.MyBusinessUnitAssociateRoleKeyOnCreation.IsNull() {
+ draft.MyBusinessUnitAssociateRoleOnCreation = &platform.AssociateRoleKeyReference{
+ Key: b.MyBusinessUnitAssociateRoleKeyOnCreation.ValueString(),
+ }
+ }
+
+ return draft
+}
+
+func (b BusinessUnits) isDefault() bool {
+ return b.MyBusinessUnitStatusOnCreation.ValueString() == string(platform.BusinessUnitStatusActive) &&
+ b.MyBusinessUnitAssociateRoleKeyOnCreation.IsNull()
+}
+
+type AssociateRoleKeyReference struct {
+ Key types.String `tfsdk:"key"`
+}
+
+func (r AssociateRoleKeyReference) toNative() *platform.AssociateRoleKeyReference {
+ return &platform.AssociateRoleKeyReference{
+ Key: r.Key.ValueString(),
+ }
+}
diff --git a/internal/resources/project/model_test.go b/internal/resources/project/model_test.go
index 1532e8d0..d42b1b3d 100644
--- a/internal/resources/project/model_test.go
+++ b/internal/resources/project/model_test.go
@@ -49,6 +49,7 @@ func TestNewProjectFromNative(t *testing.T) {
},
},
ShippingRateCartClassificationValue: []models.CustomFieldLocalizedEnumValue{},
+ BusinessUnits: []BusinessUnits{},
},
},
}
@@ -175,10 +176,40 @@ func TestUpdateActions(t *testing.T) {
},
},
},
+ {
+ name: "Create with business unit settings",
+ state: Project{
+ Version: types.Int64Value(1),
+ BusinessUnits: []BusinessUnits{},
+ },
+ plan: Project{
+ Version: types.Int64Value(1),
+ BusinessUnits: []BusinessUnits{
+ {
+ MyBusinessUnitStatusOnCreation: types.StringValue(string(platform.BusinessUnitConfigurationStatusActive)),
+ MyBusinessUnitAssociateRoleKeyOnCreation: types.StringValue("my-associate-role"),
+ },
+ },
+ },
+ action: platform.ProjectUpdate{
+ Version: 1,
+ Actions: []platform.ProjectUpdateAction{
+ platform.ProjectChangeBusinessUnitStatusOnCreationAction{
+ Status: platform.BusinessUnitConfigurationStatusActive,
+ },
+ platform.ProjectSetBusinessUnitAssociateRoleOnCreationAction{
+ AssociateRole: platform.AssociateRoleResourceIdentifier{
+ Key: utils.StringRef("my-associate-role"),
+ },
+ },
+ },
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result := tt.state.updateActions(tt.plan)
+ result, err := tt.state.updateActions(tt.plan)
+ assert.NoError(t, err)
assert.Equal(t, tt.action, result)
})
}
@@ -246,6 +277,25 @@ func TestSetStateData(t *testing.T) {
},
Carts: nil,
},
+ }, {
+ name: "business unit in plan",
+ state: Project{
+ BusinessUnits: []BusinessUnits{
+ {
+ MyBusinessUnitStatusOnCreation: types.StringValue(string(platform.BusinessUnitConfigurationStatusInactive)),
+ },
+ },
+ Carts: []Carts{
+ {},
+ },
+ },
+ plan: Project{
+ BusinessUnits: []BusinessUnits{},
+ },
+ expected: Project{
+ BusinessUnits: nil,
+ Carts: nil,
+ },
},
}
for _, tt := range tests {
diff --git a/internal/resources/project/resource.go b/internal/resources/project/resource.go
index 9c5ca44f..937696e9 100644
--- a/internal/resources/project/resource.go
+++ b/internal/resources/project/resource.go
@@ -2,6 +2,7 @@ package project
import (
"context"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
@@ -27,28 +28,28 @@ import (
// Ensure the implementation satisfies the expected interfaces.
var (
- _ resource.Resource = &ProjectResource{}
- _ resource.ResourceWithConfigure = &ProjectResource{}
- _ resource.ResourceWithImportState = &ProjectResource{}
+ _ resource.Resource = &projectResource{}
+ _ resource.ResourceWithConfigure = &projectResource{}
+ _ resource.ResourceWithImportState = &projectResource{}
)
-// NewOrderResource is a helper function to simplify the provider implementation.
+// NewResource is a helper function to simplify the provider implementation.
func NewResource() resource.Resource {
- return &ProjectResource{}
+ return &projectResource{}
}
-// orderResource is the resource implementation.
-type ProjectResource struct {
+// projectResource is the resource implementation.
+type projectResource struct {
client *platform.ByProjectKeyRequestBuilder
}
// Metadata returns the data source type name.
-func (r *ProjectResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+func (r *projectResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_project_settings"
}
// Schema defines the schema for the data source.
-func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
+func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "The project endpoint provides a limited set of information about settings and configuration of " +
"the project. Updating the settings is eventually consistent, it may take up to a minute before " +
@@ -58,32 +59,32 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
Attributes: map[string]schema.Attribute{
// The ID is only here to make testing framework happy.
"id": schema.StringAttribute{
- Description: "The unique key of the project",
- Computed: true,
+ MarkdownDescription: "The unique key of the project",
+ Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"key": schema.StringAttribute{
- Description: "The unique key of the project",
- Computed: true,
+ MarkdownDescription: "The unique key of the project",
+ Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"name": schema.StringAttribute{
- Description: "The name of the project",
- Optional: true,
- Computed: true,
+ MarkdownDescription: "The name of the project",
+ Optional: true,
+ Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"currencies": schema.ListAttribute{
- ElementType: types.StringType,
- Description: "A three-digit currency code as per [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217)",
- Optional: true,
- Computed: true,
+ ElementType: types.StringType,
+ MarkdownDescription: "A three-digit currency code as per [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217)",
+ Optional: true,
+ Computed: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.UseStateForUnknown(),
},
@@ -92,10 +93,10 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
"countries": schema.ListAttribute{
- ElementType: types.StringType,
- Description: "A two-digit country code as per [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)",
- Optional: true,
- Computed: true,
+ ElementType: types.StringType,
+ MarkdownDescription: "A two-digit country code as per [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)",
+ Optional: true,
+ Computed: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.UseStateForUnknown(),
},
@@ -113,23 +114,23 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
"enable_search_index_products": schema.BoolAttribute{
- Description: "Enable the Search Indexing of products",
- Optional: true,
- Computed: true,
+ MarkdownDescription: "Enable the Search Indexing of products",
+ Optional: true,
+ Computed: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.UseStateForUnknown(),
},
},
"enable_search_index_orders": schema.BoolAttribute{
- Description: "Enable the Search Indexing of orders",
- Optional: true,
- Computed: true,
+ MarkdownDescription: "Enable the Search Indexing of orders",
+ Optional: true,
+ Computed: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.UseStateForUnknown(),
},
},
"shipping_rate_input_type": schema.StringAttribute{
- Description: "Three ways to dynamically select a ShippingRatePriceTier exist. The CartValue type uses " +
+ MarkdownDescription: "Three ways to dynamically select a ShippingRatePriceTier exist. The CartValue type uses " +
"the sum of all line item prices, whereas CartClassification and CartScore use the " +
"shippingRateInput field on the cart to select a tier",
Optional: true,
@@ -155,13 +156,13 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"country_tax_rate_fallback_enabled": schema.BoolAttribute{
- Description: "Indicates if country - no state tax rate fallback should be used when a " +
+ MarkdownDescription: "Indicates if country - no state tax rate fallback should be used when a " +
"shipping address state is not explicitly covered in the rates lists of all tax " +
"categories of a cart line items",
Optional: true,
},
"delete_days_after_last_modification": schema.Int64Attribute{
- Description: "Number - Optional The default value for the " +
+ MarkdownDescription: "Number - Optional The default value for the " +
"deleteDaysAfterLastModification parameter of the CartDraft. Initially set to 90 for " +
"projects created after December 2019.",
Optional: true,
@@ -173,19 +174,19 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
"messages": schema.ListNestedBlock{
- Description: "The change notifications subscribed to",
+ MarkdownDescription: "The change notifications subscribed to",
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
- Description: "When true the creation of messages on the Messages Query HTTP API is enabled",
- Optional: true,
+ MarkdownDescription: "When true the creation of messages on the Messages Query HTTP API is enabled",
+ Optional: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.UseStateForUnknown(),
},
},
"delete_days_after_creation": schema.Int64Attribute{
- Description: "Specifies the number of days each Message should be available via the Messages Query API",
- Optional: true,
+ MarkdownDescription: "Specifies the number of days each Message should be available via the Messages Query API",
+ Optional: true,
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
@@ -213,8 +214,8 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
"authorization_header": schema.StringAttribute{
- Description: "Partially hidden on retrieval",
- Optional: true,
+ MarkdownDescription: "Partially hidden on retrieval",
+ Optional: true,
Validators: []validator.String{
stringvalidator.AlsoRequires(
path.MatchRelative().AtParent().AtName("url"),
@@ -228,11 +229,12 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
"shipping_rate_cart_classification_value": schema.ListNestedBlock{
- Description: "If shipping_rate_input_type is set to CartClassification these values are used to create " +
+ MarkdownDescription: "If shipping_rate_input_type is set to CartClassification these values are used to create " +
"tiers\n. Only a key defined inside the values array can be used to create a tier, or to set a value " +
"for the shippingRateInput on the cart. The keys are checked for uniqueness and the request is " +
"rejected if keys are not unique",
Validators: []validator.List{
+ listvalidator.SizeAtMost(1),
customvalidator.RequireValueValidator(
"CartClassification",
path.MatchRoot("shipping_rate_input_type"),
@@ -250,12 +252,40 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
},
+ "business_units": schema.ListNestedBlock{
+ MarkdownDescription: "Holds configuration specific to [Business Units](https://docs.commercetools.com/api/projects/business-units#ctp:api:type:BusinessUnit).",
+ Validators: []validator.List{
+ listvalidator.SizeAtMost(1),
+ },
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "my_business_unit_status_on_creation": schema.StringAttribute{
+ MarkdownDescription: "Status of Business Units created using the My Business Unit endpoint.",
+ Computed: true,
+ Optional: true,
+ Default: stringdefault.StaticString(string(platform.BusinessUnitConfigurationStatusInactive)),
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ string(platform.BusinessUnitConfigurationStatusActive),
+ string(platform.BusinessUnitConfigurationStatusInactive),
+ ),
+ },
+ },
+ "my_business_unit_associate_role_key_on_creation": schema.StringAttribute{
+ MarkdownDescription: "Default Associate Role assigned to the Associate creating a " +
+ "Business Unit using the My Business Unit endpoint. Note that this field cannot be " +
+ "unset once assigned!",
+ Optional: true,
+ },
+ },
+ },
+ },
},
}
}
// Configure adds the provider configured client to the data source.
-func (r *ProjectResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
+func (r *projectResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
@@ -263,7 +293,7 @@ func (r *ProjectResource) Configure(_ context.Context, req resource.ConfigureReq
r.client = data.Client
}
-func (p *ProjectResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
+func (r *projectResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
StateUpgrader: upgradeStateV0,
@@ -272,7 +302,7 @@ func (p *ProjectResource) UpgradeState(ctx context.Context) map[int64]resource.S
}
// Create creates the resource and sets the initial Terraform state.
-func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+func (r *projectResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Retrieve values from plan
var plan Project
diags := req.Plan.Get(ctx, &plan)
@@ -291,7 +321,12 @@ func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest
}
current := NewProjectFromNative(project)
- input := current.updateActions(plan)
+ input, err := current.updateActions(plan)
+ if err != nil {
+ resp.Diagnostics.AddError("Error updating project", err.Error())
+ return
+ }
+
var res *platform.Project
err = sdk_resource.RetryContext(ctx, 5*time.Second, func() *sdk_resource.RetryError {
var err error
@@ -315,7 +350,7 @@ func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest
}
// Read refreshes the Terraform state with the latest data.
-func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+func (r *projectResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
// Get current state
var state Project
diags := req.State.Get(ctx, &state)
@@ -344,7 +379,7 @@ func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, re
}
// Update updates the resource and sets the updated Terraform state on success.
-func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+func (r *projectResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
// Retrieve values from plan
var plan Project
diags := req.Plan.Get(ctx, &plan)
@@ -361,10 +396,14 @@ func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest
return
}
- input := state.updateActions(plan)
+ input, err := state.updateActions(plan)
+ if err != nil {
+ resp.Diagnostics.AddError("Error updating project", err.Error())
+ return
+ }
var res *platform.Project
- err := sdk_resource.RetryContext(ctx, 5*time.Second, func() *sdk_resource.RetryError {
+ err = sdk_resource.RetryContext(ctx, 5*time.Second, func() *sdk_resource.RetryError {
var err error
res, err = r.client.Post(input).Execute(ctx)
return utils.ProcessRemoteError(err)
@@ -384,7 +423,7 @@ func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest
}
// Delete deletes the resource and removes the Terraform state on success.
-func (r *ProjectResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+func (r *projectResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
// Retrieve values from state
var state Project
diags := req.State.Get(ctx, &state)
@@ -395,7 +434,7 @@ func (r *ProjectResource) Delete(ctx context.Context, req resource.DeleteRequest
}
-func (r *ProjectResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+func (r *projectResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
diff --git a/internal/sharedtypes/address.go b/internal/sharedtypes/address.go
new file mode 100644
index 00000000..ce85ed76
--- /dev/null
+++ b/internal/sharedtypes/address.go
@@ -0,0 +1,301 @@
+package sharedtypes
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/labd/commercetools-go-sdk/platform"
+ "slices"
+)
+
+var (
+ AddressBlockSchema = schema.ListNestedBlock{
+ MarkdownDescription: "Addresses used by the Business Unit.",
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "Unique identifier of the Address",
+ Computed: true,
+ },
+ "key": schema.StringAttribute{
+ MarkdownDescription: "User-defined identifier of the Address that must be unique when multiple " +
+ "addresses are referenced in BusinessUnits, Customers, and itemShippingAddresses " +
+ "(LineItem-specific addresses) of a Cart, Order, QuoteRequest, or Quote.",
+ Required: true,
+ },
+ "external_id": schema.StringAttribute{
+ MarkdownDescription: "ID for the contact used in an external system",
+ Optional: true,
+ },
+ "country": schema.StringAttribute{
+ MarkdownDescription: "Name of the country",
+ Required: true,
+ },
+ "title": schema.StringAttribute{
+ MarkdownDescription: "Title of the contact, for example Dr., Prof.",
+ Optional: true,
+ },
+ "salutation": schema.StringAttribute{
+ MarkdownDescription: "Salutation of the contact, for example Ms., Mr.",
+ Optional: true,
+ },
+ "first_name": schema.StringAttribute{
+ MarkdownDescription: "First name of the contact",
+ Optional: true,
+ },
+ "last_name": schema.StringAttribute{
+ MarkdownDescription: "Last name of the contact",
+ Optional: true,
+ },
+ "street_name": schema.StringAttribute{
+ MarkdownDescription: "Name of the street",
+ Optional: true,
+ },
+ "street_number": schema.StringAttribute{
+ MarkdownDescription: "Street number",
+ Optional: true,
+ },
+ "additional_street_info": schema.StringAttribute{
+ MarkdownDescription: "Further information on the street address",
+ Optional: true,
+ },
+ "postal_code": schema.StringAttribute{
+ MarkdownDescription: "Postal code",
+ Optional: true,
+ },
+ "city": schema.StringAttribute{
+ MarkdownDescription: "Name of the city",
+ Optional: true,
+ },
+ "region": schema.StringAttribute{
+ MarkdownDescription: "Name of the region",
+ Optional: true,
+ },
+ "state": schema.StringAttribute{
+ MarkdownDescription: "Name of the state",
+ Optional: true,
+ },
+ "company": schema.StringAttribute{
+ MarkdownDescription: "Name of the company",
+ Optional: true,
+ },
+ "department": schema.StringAttribute{
+ MarkdownDescription: "Name of the department",
+ Optional: true,
+ },
+ "building": schema.StringAttribute{
+ MarkdownDescription: "Name or number of the building",
+ Optional: true,
+ },
+ "apartment": schema.StringAttribute{
+ MarkdownDescription: "Name or number of the apartment",
+ Optional: true,
+ },
+ "po_box": schema.StringAttribute{
+ MarkdownDescription: "Post office box number",
+ Optional: true,
+ },
+ "phone": schema.StringAttribute{
+ MarkdownDescription: "Phone number",
+ Optional: true,
+ },
+ "mobile": schema.StringAttribute{
+ MarkdownDescription: "Mobile phone number",
+ Optional: true,
+ },
+ "email": schema.StringAttribute{
+ MarkdownDescription: "Email address",
+ Optional: true,
+ },
+ "fax": schema.StringAttribute{
+ MarkdownDescription: "Fax number",
+ Optional: true,
+ },
+ "additional_address_info": schema.StringAttribute{
+ MarkdownDescription: "Further information on the Address",
+ Optional: true,
+ },
+ },
+ },
+ }
+)
+
+/*
+ Support types for the business unit resource.
+*/
+
+type Address struct {
+ ID types.String `tfsdk:"id"`
+ Key types.String `tfsdk:"key"`
+ ExternalID types.String `tfsdk:"external_id"`
+ Country types.String `tfsdk:"country"`
+ Title types.String `tfsdk:"title"`
+ Salutation types.String `tfsdk:"salutation"`
+ FirstName types.String `tfsdk:"first_name"`
+ LastName types.String `tfsdk:"last_name"`
+ StreetName types.String `tfsdk:"street_name"`
+ StreetNumber types.String `tfsdk:"street_number"`
+ AdditionalStreetInfo types.String `tfsdk:"additional_street_info"`
+ PostalCode types.String `tfsdk:"postal_code"`
+ City types.String `tfsdk:"city"`
+ Region types.String `tfsdk:"region"`
+ State types.String `tfsdk:"state"`
+ Company types.String `tfsdk:"company"`
+ Department types.String `tfsdk:"department"`
+ Building types.String `tfsdk:"building"`
+ Apartment types.String `tfsdk:"apartment"`
+ POBox types.String `tfsdk:"po_box"`
+ Phone types.String `tfsdk:"phone"`
+ Mobile types.String `tfsdk:"mobile"`
+ Email types.String `tfsdk:"email"`
+ Fax types.String `tfsdk:"fax"`
+ AdditionalAddressInfo types.String `tfsdk:"additional_address_info"`
+}
+
+func (a Address) Equal(other Address) bool {
+ return a.Key.Equal(other.Key) &&
+ a.ExternalID.Equal(other.ExternalID) &&
+ a.Country.Equal(other.Country) &&
+ a.Title.Equal(other.Title) &&
+ a.Salutation.Equal(other.Salutation) &&
+ a.FirstName.Equal(other.FirstName) &&
+ a.LastName.Equal(other.LastName) &&
+ a.StreetName.Equal(other.StreetName) &&
+ a.StreetNumber.Equal(other.StreetNumber) &&
+ a.AdditionalStreetInfo.Equal(other.AdditionalStreetInfo) &&
+ a.PostalCode.Equal(other.PostalCode) &&
+ a.City.Equal(other.City) &&
+ a.Region.Equal(other.Region) &&
+ a.State.Equal(other.State) &&
+ a.Company.Equal(other.Company) &&
+ a.Department.Equal(other.Department) &&
+ a.Building.Equal(other.Building) &&
+ a.Apartment.Equal(other.Apartment) &&
+ a.POBox.Equal(other.POBox) &&
+ a.Phone.Equal(other.Phone) &&
+ a.Mobile.Equal(other.Mobile) &&
+ a.Email.Equal(other.Email) &&
+ a.Fax.Equal(other.Fax) &&
+ a.AdditionalAddressInfo.Equal(other.AdditionalAddressInfo)
+}
+
+func (a Address) Draft() platform.BaseAddress {
+ return platform.BaseAddress{
+ Key: a.Key.ValueStringPointer(),
+ ExternalId: a.ExternalID.ValueStringPointer(),
+ Country: a.Country.ValueString(),
+ Title: a.Title.ValueStringPointer(),
+ Salutation: a.Salutation.ValueStringPointer(),
+ FirstName: a.FirstName.ValueStringPointer(),
+ LastName: a.LastName.ValueStringPointer(),
+ StreetName: a.StreetName.ValueStringPointer(),
+ StreetNumber: a.StreetNumber.ValueStringPointer(),
+ AdditionalStreetInfo: a.AdditionalStreetInfo.ValueStringPointer(),
+ PostalCode: a.PostalCode.ValueStringPointer(),
+ City: a.City.ValueStringPointer(),
+ Region: a.Region.ValueStringPointer(),
+ State: a.State.ValueStringPointer(),
+ Company: a.Company.ValueStringPointer(),
+ Department: a.Department.ValueStringPointer(),
+ Building: a.Building.ValueStringPointer(),
+ Apartment: a.Apartment.ValueStringPointer(),
+ POBox: a.POBox.ValueStringPointer(),
+ Phone: a.Phone.ValueStringPointer(),
+ Mobile: a.Mobile.ValueStringPointer(),
+ Email: a.Email.ValueStringPointer(),
+ Fax: a.Fax.ValueStringPointer(),
+ AdditionalAddressInfo: a.AdditionalAddressInfo.ValueStringPointer(),
+ }
+}
+
+// NewAddressFromNative creates a new Address from a platform.Address.
+func NewAddressFromNative(a *platform.Address) Address {
+ return Address{
+ ID: types.StringPointerValue(a.ID),
+ Key: types.StringPointerValue(a.Key),
+ ExternalID: types.StringPointerValue(a.ExternalId),
+ Country: types.StringValue(a.Country),
+ Title: types.StringPointerValue(a.Title),
+ Salutation: types.StringPointerValue(a.Salutation),
+ FirstName: types.StringPointerValue(a.FirstName),
+ LastName: types.StringPointerValue(a.LastName),
+ StreetName: types.StringPointerValue(a.StreetName),
+ StreetNumber: types.StringPointerValue(a.StreetNumber),
+ AdditionalStreetInfo: types.StringPointerValue(a.AdditionalStreetInfo),
+ PostalCode: types.StringPointerValue(a.PostalCode),
+ City: types.StringPointerValue(a.City),
+ Region: types.StringPointerValue(a.Region),
+ State: types.StringPointerValue(a.State),
+ Company: types.StringPointerValue(a.Company),
+ Department: types.StringPointerValue(a.Department),
+ Building: types.StringPointerValue(a.Building),
+ Apartment: types.StringPointerValue(a.Apartment),
+ POBox: types.StringPointerValue(a.POBox),
+ Phone: types.StringPointerValue(a.Phone),
+ Mobile: types.StringPointerValue(a.Mobile),
+ Email: types.StringPointerValue(a.Email),
+ Fax: types.StringPointerValue(a.Fax),
+ AdditionalAddressInfo: types.StringPointerValue(a.AdditionalAddressInfo),
+ }
+}
+
+type DeleteAddressAction interface {
+ platform.BusinessUnitRemoveAddressAction
+}
+
+type AddAddressAction interface {
+ platform.BusinessUnitAddAddressAction
+}
+
+type ChangeAddressAction interface {
+ platform.BusinessUnitChangeAddressAction
+}
+
+func AddressesAddActions[A AddAddressAction](currentAddresses []Address, plannedAddresses []Address) []any {
+ var actions []any
+
+ for _, pa := range plannedAddresses {
+ if !slices.ContainsFunc(currentAddresses, func(ca Address) bool { return pa.Key.Equal(ca.Key) }) {
+ actions = append(actions, A{
+ Address: pa.Draft(),
+ })
+ }
+ }
+
+ return actions
+}
+
+func AddressesDeleteActions[D DeleteAddressAction](currentAddresses []Address, plannedAddresses []Address) []any {
+ var actions []any
+
+ for _, ca := range currentAddresses {
+ if !slices.ContainsFunc(plannedAddresses, func(pa Address) bool { return ca.Key.Equal(pa.Key) }) {
+ actions = append(actions, D{
+ AddressKey: ca.Key.ValueStringPointer(),
+ })
+ }
+ }
+
+ return actions
+}
+
+func AddressesChangeActions[C ChangeAddressAction](currentAddresses []Address, plannedAddresses []Address) []any {
+ var actions []any
+
+ for _, ca := range currentAddresses {
+ pai := slices.IndexFunc(plannedAddresses, func(pa Address) bool { return ca.Key.Equal(pa.Key) })
+ if pai == -1 {
+ continue
+ }
+
+ pa := plannedAddresses[pai]
+
+ if !ca.Equal(pa) {
+ actions = append(actions, C{
+ AddressKey: pa.Key.ValueStringPointer(),
+ Address: pa.Draft(),
+ })
+ }
+ }
+
+ return actions
+}
diff --git a/internal/sharedtypes/store_key_reference.go b/internal/sharedtypes/store_key_reference.go
new file mode 100644
index 00000000..8eba99c3
--- /dev/null
+++ b/internal/sharedtypes/store_key_reference.go
@@ -0,0 +1,35 @@
+package sharedtypes
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/labd/commercetools-go-sdk/platform"
+)
+
+var (
+ StoreKeyReferenceBlockSchema = schema.ListNestedBlock{
+ MarkdownDescription: "Sets the Stores the Business Unit is associated with. \n\nIf the Business Unit has Stores defined, " +
+ "then all of its Carts, Orders, Quotes, or Quote Requests must belong to one of the Business Unit's " +
+ "Stores.\n\nIf the Business Unit has no Stores, then all of its Carts, Orders, Quotes, or Quote Requests " +
+ "must not belong to any Store.",
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "key": schema.StringAttribute{
+ MarkdownDescription: "User-defined unique identifier of the Store",
+ Optional: true,
+ },
+ },
+ },
+ }
+)
+
+// StoreKeyReference is a type to model the fields that all types of StoreKeyReference have in common.
+type StoreKeyReference struct {
+ Key types.String `tfsdk:"key"`
+}
+
+func NewStoreKeyReferenceFromNative(n *platform.StoreKeyReference) StoreKeyReference {
+ return StoreKeyReference{
+ Key: types.StringValue(n.Key),
+ }
+}
diff --git a/internal/utils/decode.go b/internal/utils/decode.go
new file mode 100644
index 00000000..db403cac
--- /dev/null
+++ b/internal/utils/decode.go
@@ -0,0 +1,63 @@
+package utils
+
+import (
+ "github.com/mitchellh/mapstructure"
+ "reflect"
+ "time"
+)
+
+func toTimeHookFunc() mapstructure.DecodeHookFunc {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data interface{}) (interface{}, error) {
+ if t != reflect.TypeOf(time.Time{}) {
+ return data, nil
+ }
+
+ switch f.Kind() {
+ case reflect.String:
+ return time.Parse(time.RFC3339, data.(string))
+ case reflect.Float64:
+ return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
+ case reflect.Int64:
+ return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
+ default:
+ return data, nil
+ }
+ // Convert it by parsing
+ }
+}
+
+func DecodeStruct(input interface{}, result interface{}) error {
+ meta := &mapstructure.Metadata{}
+ decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
+ Metadata: meta,
+ DecodeHook: mapstructure.ComposeDecodeHookFunc(
+ toTimeHookFunc()),
+ Result: result,
+ })
+ if err != nil {
+ return err
+ }
+
+ if err := decoder.Decode(input); err != nil {
+ return err
+ }
+
+ if val, ok := result.(Decoder); ok {
+ if raw, ok := input.(map[string]interface{}); ok {
+ unused := make(map[string]interface{})
+ for _, key := range meta.Unused {
+ unused[key] = raw[key]
+ }
+ val.DecodeStruct(unused)
+ }
+ }
+
+ return err
+}
+
+type Decoder interface {
+ DecodeStruct(map[string]interface{}) error
+}
diff --git a/internal/utils/fields.go b/internal/utils/fields.go
index 236772b1..9f73dd97 100644
--- a/internal/utils/fields.go
+++ b/internal/utils/fields.go
@@ -81,3 +81,7 @@ func BoolRef(value any) *bool {
result := value.(bool)
return &result
}
+
+func Ref[T comparable](value T) *T {
+ return &value
+}