more services

v0.1.48
Vernon Keenan 2021-02-01 20:19:16 -08:00
parent e777b6a6cc
commit 50901b818f
14 changed files with 496 additions and 7 deletions

View File

@ -8,6 +8,44 @@ import (
"code.tnxs.net/taxnexus/lib/api/geo/geo_models"
)
// GetDefaultDeliveryAddress is an account helper function
func GetDefaultDeliveryAddress(obj *Account, principal *User) (*Address, error) {
var deliveryAddress Address
theContact, err := GetContactByID(obj.DefaultDeliveryContactID, principal)
if err != nil {
sugar.Errorf("ops.getDefaultAddress: 💣 ⛔ theAccount.Defaultdeliverycontactid invalid %w", err)
if obj.ShippingAddress == nil {
if obj.BillingAddress == nil {
return nil, fmt.Errorf("ops.GetDefaultDeliveryAddress: 💣 ⛔ can't find any valid addresses")
}
deliveryAddress = *obj.BillingAddress
} else {
deliveryAddress = *obj.ShippingAddress
}
} else {
if theContact.MailingAddress == nil {
sugar.Errorf(
"ops.getDefaultDeliveryAddress: 💣 ⛔ default delivery contact has no email, id = %s",
obj.DefaultBackendID,
)
if obj.ShippingAddress == nil {
if obj.BillingAddress == nil {
return nil, fmt.Errorf("ops.GetDefaultDeliveryAddress: 💣 ⛔ can't find any valid addresses")
}
deliveryAddress = *obj.BillingAddress
} else {
deliveryAddress = *obj.ShippingAddress
}
} else {
deliveryAddress = *theContact.MailingAddress
}
}
if deliveryAddress.City == "" || deliveryAddress.State+deliveryAddress.StateCode == "" {
return nil, fmt.Errorf("ops.getDefaultDeliveryAddress: 💣 ⛔ city or state+statecode is blank: %v", deliveryAddress)
}
return &deliveryAddress, nil
}
// GetAccount is first class retrieval function
func GetAccount(key string, principal *User) *Account {
if key == "" {
@ -26,13 +64,13 @@ func GetAccount(key string, principal *User) *Account {
// GetAccountByID retrieves and enriches an Account object instance
func GetAccountByID(recordID string, principal *User) (*Account, error) {
sugar.Debug("app.GetAccountByID: 📥")
sugar.Debug("GetAccountByID: 📥")
if recordID == "" {
return nil, fmt.Errorf("app.getAccountByID: 💣 ⛔ key is blank")
return nil, fmt.Errorf("getAccountByID: 💣 ⛔ key is blank")
}
obj, ok := accountCache.get(recordID)
if ok {
sugar.Debug("app.getAccountByID: 👍 🎯 📤")
sugar.Debug("getAccountByID: 👍 🎯 📤")
return obj, nil
}
crmParams := accounts.NewGetAccountsParamsWithTimeout(getTimeout)
@ -47,7 +85,7 @@ func GetAccountByID(recordID string, principal *User) (*Account, error) {
}
finalObj := theObj.Enrich(principal)
accountCache.put(recordID, finalObj)
sugar.Debug("app.getAccountByID: 👍 🆕 📤")
sugar.Debug("getAccountByID: 👍 🆕 📤")
return finalObj, nil
}
@ -153,7 +191,7 @@ func (obj *Account) Enrich(principal *User) *Account {
// GetCoordinate is a first class retrieval function
func (obj *Account) GetCoordinate(principal *User) *Coordinate {
sugar.Debug("app.Account.getCoordinate: 📥")
sugar.Debug("Account.getCoordinate: 📥")
if obj.CoordinateID != "" { // if CoordinateID is set, then just get it
geoParams := coordinate.NewGetCoordinatesParamsWithTimeout(getTimeout)
geoParams.CoordinateID = &obj.CoordinateID
@ -168,7 +206,7 @@ func (obj *Account) GetCoordinate(principal *User) *Coordinate {
return obj
} // else get it via addresses, shipping #1, Business #2
if obj.ShippingAddress.ToString() == "" && obj.BillingAddress.ToString() == "" {
sugar.Errorf("app.Account.getCoordinate: 💣 ⛔ billing and shipping address both blank")
sugar.Errorf("Account.getCoordinate: 💣 ⛔ billing and shipping address both blank")
return nil
}
theAddress := obj.ShippingAddress.ToString()
@ -186,6 +224,6 @@ func (obj *Account) GetCoordinate(principal *User) *Coordinate {
for _, itm := range response.Payload.Data { // single iteration execution
swag = itm
}
sugar.Debug("app.Account.getCoordinate: 👍 📤")
sugar.Debug("Account.getCoordinate: 👍 📤")
return UnMarshalCoordinate(swag, principal)
}

View File

@ -0,0 +1,28 @@
package app
import (
"sync"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_models"
)
var accountingRuleCache = accountingRuleCacheType{
obj: map[string]map[string]*ledger_models.AccountingRule{},
}
type accountingRuleCacheType struct {
sync.RWMutex
obj map[string]map[string]*ledger_models.AccountingRule
}
func (m *accountingRuleCacheType) get(accountID string) (map[string]*ledger_models.AccountingRule, bool) {
m.RLock()
defer m.RUnlock()
r, ok := m.obj[accountID]
return r, ok
}
func (m *accountingRuleCacheType) put(accountID string, rules map[string]*ledger_models.AccountingRule) {
m.Lock()
defer m.Unlock()
m.obj[accountID] = rules
}

View File

@ -0,0 +1,34 @@
package app
import (
"fmt"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_client/accounting_rule"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_models"
)
// GetAccountingRulesByAccountID is a first class object retrieval method
func GetAccountingRulesByAccountID(
accountID string,
principal *User,
) (map[string]*ledger_models.AccountingRule, error) {
sugar.Debug("ops.getAccountingRulesByAccountID: 📥")
obj, ok := accountingRuleCache.get(accountID)
if ok {
sugar.Debugf("ops.getAccountingRulesByAccountID: 👍 📤 🎯 n = %v", len(obj))
return obj, nil
}
ledgerParams := accounting_rule.NewGetAccountingRulesParamsWithTimeout(getTimeout)
ledgerParams.AccountID = accountID
response, restErr := ledgerClient.AccountingRule.GetAccountingRules(ledgerParams, principal.Auth)
if restErr != nil {
return nil, fmt.Errorf("ops.getAccountingRulesByAccountID: 💣 ⛔ %w", restErr)
}
theRules := map[string]*ledger_models.AccountingRule{}
for _, itm := range response.Payload.Data {
theRules[itm.Code] = itm
}
sugar.Debugf("ops.getAccountingRulesByAccountID: 👍 📤 n = %v", len(theRules))
accountingRuleCache.put(accountID, theRules)
return theRules, nil
}

View File

@ -0,0 +1,29 @@
package app
import (
"sync"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_models"
)
var accountingRulesetCache = accountingRulesetCacheType{
obj: map[string]map[string]*ledger_models.AccountingRuleset{},
}
type accountingRulesetCacheType struct {
sync.RWMutex
obj map[string]map[string]*ledger_models.AccountingRuleset
}
func (m *accountingRulesetCacheType) get(accountID string) (map[string]*ledger_models.AccountingRuleset, bool) {
m.RLock()
defer m.RUnlock()
r, ok := m.obj[accountID]
return r, ok
}
func (m *accountingRulesetCacheType) put(accountID string, rules map[string]*ledger_models.AccountingRuleset) {
m.Lock()
defer m.Unlock()
m.obj[accountID] = rules
}

View File

@ -0,0 +1,32 @@
package app
import (
"fmt"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_client/accounting_ruleset"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_models"
)
func GetAccountingRulesetsByAccountID(
accountID string,
principal *User,
) (map[string]*ledger_models.AccountingRuleset, error) {
obj, ok := accountingRulesetCache.get(accountID)
if ok {
sugar.Debugf("ops.getAccountingRulesetsByAccountID: 👍 📤 🎯 n = %v", len(obj))
return obj, nil
}
params := accounting_ruleset.NewGetAccountingRulesetsParamsWithTimeout(getTimeout)
params.AccountID = accountID
response, restErr := ledgerClient.AccountingRuleset.GetAccountingRulesets(params, principal.Auth)
if restErr != nil {
return nil, fmt.Errorf("ops.getAccountingRulesetsByAccountID: 💣 ⛔ %w", restErr)
}
theRulesets := map[string]*ledger_models.AccountingRuleset{}
for _, itm := range response.Payload.Data {
theRulesets[itm.Code] = itm
}
sugar.Debugf("ops.getAccountingRulesetsByAccountID: 👍 📤 n = %v", len(theRulesets))
accountingRulesetCache.put(accountID, theRulesets)
return theRulesets, nil
}

50
app/address-services.go Normal file
View File

@ -0,0 +1,50 @@
package app
import (
"fmt"
"code.tnxs.net/taxnexus/lib/api/geo/geo_models"
)
type GeocodeAddressParams struct {
BusinessAddress *Address
Account *Account
Ref string
}
func GeocodeAddress(params GeocodeAddressParams, principal *User) (*geo_models.CoordinateBasic, error) {
if params.BusinessAddress == nil {
sugar.Infof("ops.geocodeAddress: ❗ Business Address is null, ref = %s", params.Ref)
deliveryAddress, acctErr := GetDefaultDeliveryAddress(params.Account, principal)
if acctErr != nil {
return nil, fmt.Errorf("ops.geocodeAddress: 💣 ⛔ can't determine default delivery address: %w", acctErr)
}
params.BusinessAddress = deliveryAddress
}
if params.BusinessAddress.City == "" {
sugar.Infof("ops.geocodeAddress: ❗ Business Address is blank, ref = %s", params.Ref)
deliveryAddress, acctErr := GetDefaultDeliveryAddress(params.Account, principal)
if acctErr != nil {
return nil, fmt.Errorf("ops.geocodeAddress: 💣 ⛔ can't determine default delivery address: %w", acctErr)
}
params.BusinessAddress = deliveryAddress
}
var situsCoordinate *geo_models.CoordinateBasic
aCoordinate, err := GetCoordinate(params.BusinessAddress, principal)
if err == nil {
situsCoordinate = aCoordinate
} else {
sugar.Infof("ops.geocodeAddress: ❗ can't geocode address: %w", err)
deliveryAddress, acctErr := GetDefaultDeliveryAddress(params.Account, principal)
if acctErr != nil {
return nil, fmt.Errorf("ops.geocodeAddress: 💣 ⛔ can't determine default delivery address: %w", acctErr)
}
aCoordinate, coordErr := GetCoordinate(deliveryAddress, principal)
if coordErr != nil {
return nil, fmt.Errorf("ops.geocodeAddress: 💣 ⛔ can't determine default delivery coordinates: %w", acctErr)
}
sugar.Infof("ops.geocodeAddress: ❗ alternate address used for ref = %s", params.Ref)
situsCoordinate = aCoordinate
}
return situsCoordinate, nil
}

29
app/coordinate-cache.go Normal file
View File

@ -0,0 +1,29 @@
package app
import (
"sync"
"code.tnxs.net/taxnexus/lib/api/geo/geo_models"
)
var coordinateCache = coordinateCacheType{
obj: map[string]*geo_models.CoordinateBasic{},
}
type coordinateCacheType struct {
sync.RWMutex
obj map[string]*geo_models.CoordinateBasic
}
func (m *coordinateCacheType) get(addrStr string) (*geo_models.CoordinateBasic, bool) {
m.RLock()
defer m.RUnlock()
r, ok := m.obj[addrStr]
return r, ok
}
func (m *coordinateCacheType) put(accountID string, coord *geo_models.CoordinateBasic) {
m.Lock()
defer m.Unlock()
m.obj[accountID] = coord
}

View File

@ -0,0 +1,51 @@
package app
import (
"fmt"
"code.tnxs.net/taxnexus/lib/api/geo/geo_client/coordinate"
"code.tnxs.net/taxnexus/lib/api/geo/geo_models"
)
// GetCoordinate is a first class object retrieval method
func GetCoordinate(addr *Address, principal *User) (*geo_models.CoordinateBasic, error) {
sugar.Debug("ops.getCoordinate: 📥")
if addr == nil {
return nil, fmt.Errorf("ops.getCoordinate: 💣 ⛔ Address cannot be nil")
}
if addr.ToString() == "" {
return nil, fmt.Errorf("ops.getCoordinate: 💣 ⛔ Address cannot be empty")
}
sugar.Debugf("ops.getCoordinate: 📏 address: %s", addr.ToString())
if addr.City == "" {
return nil, fmt.Errorf("ops.getCoordinate: 💣 ⛔ City cannot be blank")
}
obj, ok := coordinateCache.get(addr.ToString())
if ok {
sugar.Debugf("ops.GetCoordiante: 👍🏻 🎯 📤")
return obj, nil
}
placeName := addr.City + ", " + addr.StateCode
if addr.StateCode == "" {
placeName = addr.City + ", " + addr.State
}
coordParams := coordinate.NewGetCoordinateBasicParamsWithTimeout(getTimeout)
theAddress := addr.ToString()
coordParams.PlaceName = &placeName
coordParams.Address = &theAddress
response, geoErr := geoClient.Coordinate.GetCoordinateBasic(coordParams, principal.Auth)
if geoErr != nil {
return nil, fmt.Errorf("ops.getCoordinate: 💣 ⛔ failed, %s (%w)", *coordParams.Address, geoErr)
}
if len(response.Payload.Data) != 1 {
return nil, fmt.Errorf("ops.getCoordinate: 💣 ⛔ one and only one coordinate record should be returned")
}
sugar.Debugf("ops.getCoordinate: ✅ geo records retrieved = %d", len(response.Payload.Data))
var coord geo_models.CoordinateBasic
for _, itm := range response.Payload.Data {
coord = *itm
}
coordinateCache.put(addr.ToString(), &coord)
sugar.Debug("ops.getCoordinate: 👍 📤")
return &coord, nil
}

38
app/glbalance-cache.go Normal file
View File

@ -0,0 +1,38 @@
package app
import (
"sync"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_models"
)
var glBalanceCache = glBalanceCacheType{
obj: map[string]map[string]*ledger_models.GlBalance{},
}
type glBalanceCacheType struct {
sync.RWMutex
obj map[string]map[string]*ledger_models.GlBalance
}
func (m *glBalanceCacheType) get(periodID, glAccountID string) (*ledger_models.GlBalance, bool) {
m.RLock()
defer m.RUnlock()
r, ok := m.obj[periodID][glAccountID]
return r, ok
}
func (m *glBalanceCacheType) put(periodID, glAccountID string, glBalance *ledger_models.GlBalance) {
m.init(periodID)
m.Lock()
defer m.Unlock()
m.obj[periodID][glAccountID] = glBalance
}
func (m *glBalanceCacheType) init(periodID string) {
m.RLock()
defer m.RUnlock()
_, ok := m.obj[periodID]
if !ok {
m.obj[periodID] = map[string]*ledger_models.GlBalance{}
}
}

38
app/glbalance-services.go Normal file
View File

@ -0,0 +1,38 @@
package app
import (
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_client/gl_balance"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_models"
)
// GlBalanceParams is a parameter struct
type GlBalanceParams struct {
glAccountID string
periodID string
principal *User
}
// GetGlBalanceByParams is a GL balance helper function
func GetGlBalanceByParams(params GlBalanceParams) *ledger_models.GlBalance {
sugar.Debug("ops.getGlBalanceByParams: 📥")
obj, ok := glBalanceCache.get(params.periodID, params.glAccountID)
if ok {
sugar.Debug("ops.getGlBalanceByParams: 👍 📤 🎯")
return obj
}
ledgerParams := gl_balance.NewGetGlBalancesParamsWithTimeout(getTimeout)
ledgerParams.GlAccountID = &params.glAccountID
ledgerParams.PeriodID = &params.periodID
response, restErr := ledgerClient.GlBalance.GetGlBalances(ledgerParams, params.principal.Auth)
if restErr != nil {
sugar.Errorf("ops.getGlBalanceByParams: 💣 ⛔ %s", restErr.Error())
return nil
}
theGlBalance := &ledger_models.GlBalance{}
for _, itm := range response.Payload.Data {
theGlBalance = itm // singleton
}
glBalanceCache.put(params.periodID, params.glAccountID, theGlBalance)
sugar.Debugf("ops.getGlBalanceByParams: 👍 📤")
return theGlBalance
}

36
app/period-cache.go Normal file
View File

@ -0,0 +1,36 @@
package app
import "sync"
var periodCache = periodCacheType{
obj: map[string]map[string]*CalendarPeriod{},
}
// periodCacheType maps [accountID][dateString]
type periodCacheType struct {
sync.RWMutex
obj map[string]map[string]*CalendarPeriod
}
func (m *periodCacheType) get(accountID, dateStr string) (*CalendarPeriod, bool) {
m.RLock()
defer m.RUnlock()
r, ok := m.obj[accountID][dateStr]
return r, ok
}
func (m *periodCacheType) put(accountID, dateStr string, period *CalendarPeriod) {
m.init(accountID)
m.Lock()
defer m.Unlock()
m.obj[accountID][dateStr] = period
}
func (m *periodCacheType) init(accountID string) {
m.RLock()
defer m.RUnlock()
_, ok := m.obj[accountID]
if !ok {
m.obj[accountID] = map[string]*CalendarPeriod{}
}
}

56
app/period-services.go Normal file
View File

@ -0,0 +1,56 @@
package app
import (
"fmt"
"time"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_client/period"
)
// CalendarPeriod is a parameter type
type CalendarPeriod struct {
ID string
Name string
StartDate time.Time
EndDate time.Time
}
// GetPeriodParams is a parameter type
type GetPeriodParams struct {
Date time.Time
AccountID string
Principal *User
}
// GetPeriodIDByDate is a period helper function
func GetPeriodIDByDate(params GetPeriodParams) (string, error) {
sugar.Debug("ops.getPeriodIDByDate: 📥")
obj, ok := periodCache.get(params.AccountID, params.Date.Format(dateFormat))
if ok {
sugar.Debugf("ops.getPeriodIDByDate: 👍 📤 🎯")
return obj.ID, nil
}
ledgerDate := params.Date.Format(dateFormat)
ledgerParams := period.NewGetPeriodsParamsWithTimeout(getTimeout)
ledgerParams.AccountID = &params.AccountID
ledgerParams.Date = &ledgerDate
response, restErr := ledgerClient.Period.GetPeriods(ledgerParams, params.Principal.Auth)
if restErr != nil {
return "", fmt.Errorf("ops.getPeriodIDByDate: 💣 ⛔ dateStr = %s (%w)", params.Date, restErr)
}
// this loop should iterate just once
thePeriod := &CalendarPeriod{}
for _, itm := range response.Payload.Data {
startDate, _ := time.Parse(dateTimeFormat, itm.StartDate)
endDate, _ := time.Parse(dateTimeFormat, itm.EndDate)
thePeriod = &CalendarPeriod{
ID: itm.ID,
EndDate: endDate,
Name: itm.Name,
StartDate: startDate,
}
}
periodCache.put(params.AccountID, params.Date.Format(dateFormat), thePeriod)
sugar.Debugf("ops.getPeriodIDByDate: 👍 📤 %s", thePeriod.Name)
return thePeriod.ID, nil
}

View File

@ -9,6 +9,7 @@ import (
"code.tnxs.net/taxnexus/lib/api/auth0/auth0_client"
"code.tnxs.net/taxnexus/lib/api/crm/crm_client"
"code.tnxs.net/taxnexus/lib/api/geo/geo_client"
"code.tnxs.net/taxnexus/lib/api/ledger/ledger_client"
"code.tnxs.net/taxnexus/lib/api/ops/ops_client"
"code.tnxs.net/taxnexus/lib/api/regs/regs_client"
"code.tnxs.net/taxnexus/lib/api/stash/stash_client"
@ -27,6 +28,7 @@ var config = Configuration{}
var configured = false
var crmClient = crm_client.Default
var geoClient = geo_client.Default
var ledgerClient = ledger_client.Default
var opsClient = ops_client.Default
var regsClient = regs_client.Default
var stashClient = stash_client.Default

View File

@ -0,0 +1,28 @@
package app
import (
"code.tnxs.net/taxnexus/lib/api/regs/regs_client/transaction"
"code.tnxs.net/taxnexus/lib/api/regs/regs_models"
)
// PostTransactions is a first class object type storage method
func PostTransactions(objList []*TaxTransaction, principal *User) error {
swagList := []*regs_models.Transaction{}
for _, itm := range objList {
swagList = append(swagList, &regs_models.Transaction{
AccountID: itm.AccountID,
TaxTransactionID: itm.ID,
TaxTypeID: itm.TaxTypeID,
Valid: true,
})
}
regsParams := transaction.NewPostTransactionsParamsWithTimeout(postTimeout)
regsParams.TransactionRequest = &regs_models.TransactionRequest{
Data: swagList,
}
_, err := regsClient.Transaction.PostTransactions(regsParams, principal.Auth)
if err != nil {
return err
}
return nil
}