203 lines
5.8 KiB
Go
203 lines
5.8 KiB
Go
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 GenerateBill(order *order.Order, request *tools.APIRequest) (*Bill, error) {
|
|
// hhmmm : should get... the loop.
|
|
return &Bill{
|
|
AbstractObject: utils.AbstractObject{
|
|
Name: "bill_" + request.PeerID + "_" + time.Now().UTC().Format("2006-01-02T15:04:05"),
|
|
IsDraft: false,
|
|
},
|
|
OrderID: order.UUID,
|
|
Status: enum.PENDING,
|
|
// SubOrders: peerOrders,
|
|
}, nil
|
|
}
|
|
|
|
func DraftFirstBill(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
|