package bill import ( "sync" "time" "cloud.o-forge.io/core/oc-lib/dbs" "cloud.o-forge.io/core/oc-lib/models/common/enum" "cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/order" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/resources/purchase_resource" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" ) /* * Booking is a struct that represents a booking */ type Bill struct { utils.AbstractObject OrderID string `json:"order_id" bson:"order_id" validate:"required"` Status enum.CompletionStatus `json:"status" bson:"status" default:"0"` SubOrders map[string]*PeerOrder `json:"sub_orders" bson:"sub_orders"` Total float64 `json:"total" bson:"total" validate:"required"` } func DraftBill(order *order.Order, request *tools.APIRequest) (*Bill, error) { peers := map[string][]*PeerItemOrder{} for _, p := range order.Purchases { // TODO : if once if _, ok := peers[p.DestPeerID]; !ok { peers[p.DestPeerID] = []*PeerItemOrder{} } peers[p.DestPeerID] = append(peers[p.DestPeerID], &PeerItemOrder{ Purchase: p, Item: p.PricedItem, Quantity: 1, }) } for _, b := range order.Bookings { // TODO : if once isPurchased := false for _, p := range order.Purchases { if p.ResourceID == b.ResourceID { isPurchased = true break } } if isPurchased { continue } if _, ok := peers[b.DestPeerID]; !ok { peers[b.DestPeerID] = []*PeerItemOrder{} } peers[b.DestPeerID] = append(peers[b.DestPeerID], &PeerItemOrder{ Item: b.PricedItem, }) } peerOrders := map[string]*PeerOrder{} for peerID, items := range peers { pr, _, err := peer.NewAccessor(request).LoadOne(peerID) if err != nil { return nil, err } peerOrders[peerID] = &PeerOrder{ PeerID: peerID, BillingAddress: pr.(*peer.Peer).WalletAddress, Items: items, } } bill := &Bill{ AbstractObject: utils.AbstractObject{ Name: "bill_" + request.PeerID + "_" + time.Now().UTC().Format("2006-01-02T15:04:05"), IsDraft: true, }, OrderID: order.UUID, Status: enum.PENDING, SubOrders: peerOrders, } return bill.SumUpBill(request) } func (d *Bill) GetAccessor(request *tools.APIRequest) utils.Accessor { return NewAccessor(request) // Create a new instance of the accessor } func (r *Bill) StoreDraftDefault() { r.IsDraft = true } func (r *Bill) CanUpdate(set utils.DBObject) (bool, utils.DBObject) { if !r.IsDraft && r.Status != set.(*Bill).Status { return true, &Bill{Status: set.(*Bill).Status} // only state can be updated } return r.IsDraft, set } func (r *Bill) CanDelete() bool { return r.IsDraft // only draft order can be deleted } func (d *Bill) SumUpBill(request *tools.APIRequest) (*Bill, error) { for _, b := range d.SubOrders { err := b.SumUpBill(request) if err != nil { return d, err } d.Total += b.Total } return d, nil } type PeerOrder struct { Error string `json:"error,omitempty" bson:"error,omitempty"` PeerID string `json:"peer_id,omitempty" bson:"peer_id,omitempty"` Status enum.CompletionStatus `json:"status" bson:"status" default:"0"` BillingAddress string `json:"billing_address,omitempty" bson:"billing_address,omitempty"` Items []*PeerItemOrder `json:"items,omitempty" bson:"items,omitempty"` Total float64 `json:"total,omitempty" bson:"total,omitempty"` } func (d *PeerOrder) Pay(request *tools.APIRequest, response chan *PeerOrder, wg *sync.WaitGroup) { d.Status = enum.PENDING go func() { // DO SOMETHING TO PAY ON BLOCKCHAIN OR WHATEVER ON RETURN UPDATE STATUS d.Status = enum.PAID // TO REMOVE LATER IT'S A MOCK if d.Status == enum.PAID { for _, b := range d.Items { if !b.Item.IsPurchasable() { continue } accessor := purchase_resource.NewAccessor(request) accessor.StoreOne(&purchase_resource.PurchaseResource{ ResourceID: b.Item.GetID(), ResourceType: b.Item.GetType(), EndDate: b.Item.GetLocationEnd(), }) } } if d.Status != enum.PENDING { response <- d } wg.Done() }() } func (d *PeerOrder) SumUpBill(request *tools.APIRequest) error { for _, b := range d.Items { tot, err := b.GetPrice(request) // missing something if err != nil { return err } d.Total += tot } return nil } type PeerItemOrder struct { Quantity int `json:"quantity,omitempty" bson:"quantity,omitempty"` Purchase *purchase_resource.PurchaseResource `json:"purchase,omitempty" bson:"purchase,omitempty"` Item pricing.PricedItemITF `json:"item,omitempty" bson:"item,omitempty"` } func (d *PeerItemOrder) GetPrice(request *tools.APIRequest) (float64, error) { accessor := purchase_resource.NewAccessor(request) search, code, _ := accessor.Search(&dbs.Filters{ And: map[string][]dbs.Filter{ "resource_id": {{Operator: dbs.EQUAL.String(), Value: d.Item.GetID()}}, }, }, "", d.Purchase.IsDraft) if code == 200 && len(search) > 0 { for _, s := range search { if s.(*purchase_resource.PurchaseResource).EndDate == nil || time.Now().UTC().After(*s.(*purchase_resource.PurchaseResource).EndDate) { return 0, nil } } } p, err := d.Item.GetPrice() if err != nil { return 0, err } return p * float64(d.Quantity), nil } // WTF HOW TO SELECT THE RIGHT PRICE ??? // SHOULD SET A BUYING STATUS WHEN PAYMENT IS VALIDATED