Implement Sale Order creation on org creation
This commit is contained in:
parent
2579aff765
commit
5b7c26bbac
2 changed files with 186 additions and 0 deletions
152
src/saleorder_storage.go
Normal file
152
src/saleorder_storage.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
package saleorder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
organizationv1 "github.com/appuio/control-api/apis/organization/v1"
|
||||
odooclient "github.com/appuio/go-odoo"
|
||||
)
|
||||
|
||||
type Odoo16Credentials = odooclient.ClientConfig
|
||||
|
||||
type Odoo16Options struct {
|
||||
SaleOrderClientReferencePrefix string
|
||||
SaleOrderInternalNote string
|
||||
Odoo8CompatibilityMode bool
|
||||
}
|
||||
|
||||
const defaultSaleOrderState = "sale"
|
||||
|
||||
type SaleOrderStorage interface {
|
||||
CreateSaleOrder(organizationv1.Organization) (string, error)
|
||||
GetSaleOrderName(organizationv1.Organization) (string, error)
|
||||
}
|
||||
|
||||
type Odoo16Client interface {
|
||||
Read(string, []int64, *odooclient.Options, interface{}) error
|
||||
CreateSaleOrder(*odooclient.SaleOrder) (int64, error)
|
||||
FindResPartners(*odooclient.Criteria, *odooclient.Options) (*odooclient.ResPartners, error)
|
||||
}
|
||||
|
||||
type Odoo16SaleOrderStorage struct {
|
||||
client Odoo16Client
|
||||
options *Odoo16Options
|
||||
}
|
||||
|
||||
func NewOdoo16Storage(credentials *Odoo16Credentials, options *Odoo16Options) (SaleOrderStorage, error) {
|
||||
client, err := odooclient.NewClient(credentials)
|
||||
return &Odoo16SaleOrderStorage{
|
||||
client: client,
|
||||
options: options,
|
||||
}, err
|
||||
}
|
||||
|
||||
func NewOdoo16StorageFromClient(client Odoo16Client, options *Odoo16Options) SaleOrderStorage {
|
||||
return &Odoo16SaleOrderStorage{
|
||||
client: client,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Odoo16SaleOrderStorage) CreateSaleOrder(org organizationv1.Organization) (string, error) {
|
||||
beID, err := k8sIDToOdooID(org.Spec.BillingEntityRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var beRecord odooclient.ResPartner
|
||||
|
||||
fetchPartnerFieldOpts := odooclient.NewOptions().FetchFields(
|
||||
"id",
|
||||
"parent_id",
|
||||
)
|
||||
|
||||
if s.options.Odoo8CompatibilityMode {
|
||||
odoo8ID := fmt.Sprintf("__export__.res_partner_%d", beID)
|
||||
|
||||
idMatchCriteria := odooclient.NewCriteria().Add("x_odoo_8_ID", "=", odoo8ID)
|
||||
|
||||
r, err := s.client.FindResPartners(idMatchCriteria, fetchPartnerFieldOpts)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetching accounting contact by ID: %w", err)
|
||||
}
|
||||
|
||||
if len(*r) <= 0 {
|
||||
return "", fmt.Errorf("no results when fetching accounting contact by ID")
|
||||
}
|
||||
resPartners := *r
|
||||
|
||||
beRecord = resPartners[0]
|
||||
} else {
|
||||
beRecords := []odooclient.ResPartner{}
|
||||
err = s.client.Read(odooclient.ResPartnerModel, []int64{int64(beID)}, fetchPartnerFieldOpts, &beRecords)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetching accounting contact by ID: %w", err)
|
||||
}
|
||||
|
||||
if len(beRecords) <= 0 {
|
||||
return "", fmt.Errorf("no results when fetching accounting contact by ID")
|
||||
}
|
||||
|
||||
beRecord = beRecords[0]
|
||||
}
|
||||
|
||||
if beRecord.ParentId == nil {
|
||||
return "", fmt.Errorf("accounting contact %d has no parent", beRecord.Id.Get())
|
||||
}
|
||||
|
||||
var clientRef string
|
||||
if org.Spec.DisplayName != "" {
|
||||
clientRef = fmt.Sprintf("%s (%s)", s.options.SaleOrderClientReferencePrefix, org.Spec.DisplayName)
|
||||
} else {
|
||||
clientRef = fmt.Sprintf("%s (%s)", s.options.SaleOrderClientReferencePrefix, org.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
newSaleOrder := odooclient.SaleOrder{
|
||||
PartnerInvoiceId: odooclient.NewMany2One(beRecord.Id.Get(), ""),
|
||||
PartnerId: odooclient.NewMany2One(beRecord.ParentId.ID, ""),
|
||||
State: odooclient.NewSelection(defaultSaleOrderState),
|
||||
ClientOrderRef: odooclient.NewString(clientRef),
|
||||
InternalNote: odooclient.NewString(s.options.SaleOrderInternalNote),
|
||||
}
|
||||
|
||||
soID, err := s.client.CreateSaleOrder(&newSaleOrder)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating new sale order: %w", err)
|
||||
}
|
||||
|
||||
return fmt.Sprint(soID), nil
|
||||
}
|
||||
|
||||
func (s *Odoo16SaleOrderStorage) GetSaleOrderName(org organizationv1.Organization) (string, error) {
|
||||
fetchOrderFieldOpts := odooclient.NewOptions().FetchFields(
|
||||
"id",
|
||||
"name",
|
||||
)
|
||||
id, err := strconv.Atoi(org.Status.SalesOrderID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing saleOrderID %q from organization status: %w", org.Status.SalesOrderID, err)
|
||||
}
|
||||
soRecords := []odooclient.SaleOrder{}
|
||||
err = s.client.Read(odooclient.SaleOrderModel, []int64{int64(id)}, fetchOrderFieldOpts, &soRecords)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetching sale order by ID: %w", err)
|
||||
}
|
||||
|
||||
if len(soRecords) <= 0 {
|
||||
return "", fmt.Errorf("no results when fetching sale orders with ID %q", id)
|
||||
}
|
||||
|
||||
return soRecords[0].Name.Get(), nil
|
||||
|
||||
}
|
||||
|
||||
func k8sIDToOdooID(id string) (int, error) {
|
||||
if !strings.HasPrefix(id, "be-") {
|
||||
return 0, fmt.Errorf("invalid ID, missing prefix: %s", id)
|
||||
}
|
||||
|
||||
return strconv.Atoi(id[3:])
|
||||
}
|
|
@ -46,6 +46,13 @@ class Organization(ServalaModelMixin, models.Model):
|
|||
verbose_name=_("Members"),
|
||||
)
|
||||
|
||||
odoo_sale_order_id = models.IntegerField(
|
||||
null=True, blank=True, verbose_name=_("Odoo Sale Order ID")
|
||||
)
|
||||
odoo_sale_order_name = models.CharField(
|
||||
max_length=100, null=True, blank=True, verbose_name=_("Odoo Sale Order Name")
|
||||
)
|
||||
|
||||
class urls(urlman.Urls):
|
||||
base = "/org/{self.slug}/"
|
||||
details = "{base}details/"
|
||||
|
@ -67,6 +74,7 @@ class Organization(ServalaModelMixin, models.Model):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
@transaction.atomic
|
||||
def create_organization(cls, instance, owner):
|
||||
try:
|
||||
instance.origin
|
||||
|
@ -76,6 +84,32 @@ class Organization(ServalaModelMixin, models.Model):
|
|||
)
|
||||
instance.save()
|
||||
instance.set_owner(owner)
|
||||
|
||||
if (
|
||||
instance.billing_entity.odoo_company_id
|
||||
and instance.billing_entity.odoo_invoice_id
|
||||
):
|
||||
payload = {
|
||||
"partner_id": instance.billing_entity.odoo_company_id,
|
||||
"partner_invoice_id": instance.billing_entity.odoo_invoice_id,
|
||||
"state": "sale",
|
||||
"client_order_ref": f"Servala (Organization: {instance.name})",
|
||||
"note": "auto-generated by Servala Portal",
|
||||
}
|
||||
sale_order_id = CLIENT.execute("sale.order", "create", [payload])
|
||||
|
||||
sale_order_data = CLIENT.search_read(
|
||||
model="sale.order",
|
||||
domain=[["id", "=", sale_order_id]],
|
||||
fields=["name"],
|
||||
limit=1,
|
||||
)
|
||||
|
||||
instance.odoo_sale_order_id = sale_order_id
|
||||
if sale_order_data:
|
||||
instance.odoo_sale_order_name = sale_order_data[0]["name"]
|
||||
instance.save(update_fields=["odoo_sale_order_id", "odoo_sale_order_name"])
|
||||
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue