package commands import ( connection "cf-capital/module_server/handlers/connection" mail "cf-capital/module_server/handlers/email" investmentV1 "cf-capital/module_server/handlers/investment/statement-v1" server_handlers "cf-server/server/handlers" slack "cf-capital/module_server/handlers/slack" "database/sql" "encoding/json" "fmt" log "github.com/rs/zerolog/log" "net/http" "os" "reflect" "time" ) type responseBody struct { Status string `json:"status"` Message string `json:"message"` } type Accounts struct { SchemeID int64 AccountID int64 XIRR_RATE float64 InvestorID int64 InitialAmount float64 EndDate string StartDate string InterestCredited float64 } type InterestEntry struct { AccountID int64 Type string Credit float64 Debit float64 } type Customers struct { Name string Mobile int64 Scheme string AccountID int64 } func DailyCron() http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") dt := getDate() params := map[string]interface{}{ "Service": "CFC Cron Service - 0.0.1", "Status": "Running", "Date": dt.Format("2006-01-02"), } slack.SendSlack("New User Registration", params) //Cron Service - Start //Daily() //End params = map[string]interface{}{ "Service": "CFC Cron Service - 0.0.1", "Status": "Closed", "Date": dt.Format("2006-01-02"), } slack.SendSlack("New User Registration", params) server_handlers.FormatResponse(w, 200) _ = json.NewEncoder(w).Encode(responseBody{Status: "OK", Message: "Daily cron - successfully executed"}) } } func Daily() { dt := getDate() tx, ctx, db := connection.Connect() query := "select ac.scheme_id, ac.account_id, s.xirr_rate, investor_id, initial_amount, start_date, COALESCE(end_date, '') as end_date, IF ((select txn_id from transaction where status = 'SUCCESS' and txn_type = 'INTEREST_CREDIT' and date(txn_date) = DATE(NOW() - INTERVAL 1 DAY) and ac.account_id = transaction.account_id limit 1) is null, 0, 1) as interest_credited from account ac left join scheme s on ac.scheme_id = s.scheme_id where account_status = 'ACTIVE' and start_date <= DATE(NOW() - INTERVAL 1 DAY)" //query := "select ac.scheme_id, ac.account_id, s.xirr_rate, investor_id, initial_amount, start_date, COALESCE(end_date, '') as end_date, IF ((select txn_id from transaction where status = 'SUCCESS' and txn_type = 'INTEREST_CREDIT' and date(created_at) = DATE(NOW()) and ac.account_id = transaction.account_id) is null, 0, 1) as interest_credited from account ac left join scheme s on ac.scheme_id = s.scheme_id where account_status = 'ACTIVE' and s.scheme_type = 'FLEXI' and end_date = '0000-00-00' and start_date <= DATE(NOW() - INTERVAL 1 DAY)" rows, err := tx.Query(query) var accounts []Accounts var customers []Customers today := dt.Format("2006-01-02") for rows.Next() { var account Accounts err = rows.Scan(&account.SchemeID, &account.AccountID, &account.XIRR_RATE, &account.InvestorID, &account.InitialAmount, &account.StartDate, &account.EndDate, &account.InterestCredited) if account.InterestCredited == 0 { if err != nil { log.Print(err.Error()) } if len(account.EndDate) == 0 || account.EndDate == "0000-00-00" { account.EndDate = today } //account.InitialAmount = GetClosingBalance(account.AccountID) accounts = append(accounts, account) //log.Print(account.InitialAmount) //log.Print("AccountID:", account.AccountID) //log.Print("start:", account.StartDate) //log.Print("end:", account.EndDate) } else { log.Print("Interest already credited") } } if len(accounts) > 0 { log.Print("calculating interest") var investment investmentV1.FlexiPlan var interestEntries []InterestEntry for i := 0; i < len(accounts); i++ { var investmentStruct investmentV1.FlexiPlan var invest investmentV1.Invest var interestEntry InterestEntry var customer Customers investmentStruct.AccountId = accounts[i].AccountID investmentStruct.Principal = accounts[i].InitialAmount investmentStruct.MonthlyROI = accounts[i].XIRR_RATE investmentStruct.StartDate = accounts[i].StartDate investmentStruct.EndDate = accounts[i].EndDate investmentStruct.ForIndividualInvestment = accounts[i].EndDate investment, _ = investmentStruct.DailyCron() txns := getTransactionsByAccountID(tx, accounts[i].AccountID) /*log.Print(txns) dataJson, _ := json.Marshal(investment) log.Print(string(dataJson)) return*/ //----- ----- ----- ----- Multiple Interest Credit Entry START ----- ----- ----- ----- if len(investment.Payout) > 0 { //log.Print(len(investment.Payout)) payoutDateMap := make(map[string]bool) for f := 1; f < len(investment.Payout); f++ { if investment.Payout[f].Amount > 0 { payoutDate := investment.Payout[f].Date if ok := payoutDateMap[payoutDate.Format("2006-01-02")]; !ok { payoutDateMap[payoutDate.Format("2006-01-02")] = true log.Print("Interest calculating Account ID ", accounts[i].AccountID, " ", payoutDate.Format("2006-01-02")) //if payoutDate.Format("2006-01-02") < today && isInterestCredited(tx, accounts[i].AccountID, payoutDate) == 0{ if payoutDate.Format("2006-01-02") < today { if ok := txns[payoutDate.Format("2006-01-02")]; !ok { interestEntry.AccountID = accounts[i].AccountID interestEntry.Type = "Deposit" interestEntry.Credit = investment.Payout[f].Amount interestEntries = append(interestEntries, interestEntry) log.Print("creating CreatePassbook") _, err = investmentV1.CreatePassbook(interestEntry.AccountID, tx, ctx, "Interest Accrued for Date "+investment.Payout[f].Date.Format("2006-01-02"), interestEntry.Credit, 0, investment.Payout[f].PrincipalAmount) if err != nil { tx.Rollback() log.Print(err.Error()) } invest.AccountID = accounts[i].AccountID invest.Amount = interestEntry.Credit invest.PaymentMethod = 1 invest.PaymentGateway = 1 invest.MHP = accounts[i].SchemeID invest.TxnDate = investment.Payout[f].Date.Format("2006-01-02") invest.ReferenceCode = investmentV1.GetRequestID() log.Print("creating CreateTransaction") _, err = investmentV1.CreateTransaction(invest, tx, ctx, "INTEREST_CREDIT", "SUCCESS") if err != nil { log.Print(err.Error()) tx.Rollback() } if isMonthEnd() == 1 { customer.Name = "A" customer.Mobile = 0 customer.Scheme = "A" customer.AccountID = 0 customers = append(customers, customer) } } else { //log.Print("Interest already calculated ", investment.Payout[f].Date, " Account Id", accounts[i].AccountID) } } else { log.Print("Beyond date ", investment.Payout[f].Date, " Account Id", accounts[i].AccountID) } } } } } else { log.Print("No Interest accrued: Account ID ", accounts[i].AccountID) } //----- ----- ----- ----- Multiple Interest Credit Entry END ----- ----- ----- ----- //----- ----- ----- ----- Single Interest Credit Entry START ----- ----- ----- ----- /*if investment.IndividualInvestment.TotalInterest > 0 { interestEntry.AccountID = accounts[i].AccountID interestEntry.Type = "Deposit" interestEntry.Credit = investment.IndividualInvestment.TotalInterest interestEntries = append(interestEntries, interestEntry) log.Print("creating CreatePassbook") _, err = investmentV1.CreatePassbook(interestEntry.AccountID, tx, ctx, "Interest Accrued for Date "+today, interestEntry.Credit, 0) if err != nil { tx.Rollback() log.Print(err.Error()) } invest.AccountID = accounts[i].AccountID invest.Amount = interestEntry.Credit invest.PaymentMethod = 1 invest.PaymentGateway = 1 invest.MHP = accounts[i].SchemeID invest.ReferenceCode = investmentV1.GetRequestID() log.Print("creating CreateTransaction") _, err = investmentV1.CreateTransaction(invest, tx, ctx, "INTEREST_CREDIT") if err != nil { log.Print(err.Error()) tx.Rollback() } } else { log.Print("No Interest accrued: Account ID", accounts[i].AccountID) }*/ //----- ----- ----- ----- Single Interest Credit Entry END ----- ----- ----- ----- } } else { log.Print("No entries are found") } if len(customers) > 0 { //sendMonthEndCommunication(customers) } connection.Close(tx, db) } func isInterestCredited(tx *sql.Tx, accountId int64, date time.Time) int64 { //dt,_ := time.Parse("2006-01-02 15:04:05", date) row := tx.QueryRow("select txn_id from transaction where status = 'SUCCESS' and txn_type = 'INTEREST_CREDIT' and account_id = ? and date(txn_date) = ?", accountId, date.Format("2006-01-02")) var txn_id int64 err := row.Scan(&txn_id) if err != nil && err != sql.ErrNoRows { log.Print("isInterestCredited, error exeception") log.Print(err.Error()) return 1 } if txn_id > 0 { return 1 } return 0 } func getTransactionsByAccountID(tx *sql.Tx, accountId int64) map[string]bool { //dt,_ := time.Parse("2006-01-02 15:04:05", date) rows, err := tx.Query("select date(txn_date) from transaction where status = 'SUCCESS' and txn_type = 'INTEREST_CREDIT' and account_id = ?", accountId) transactions := make(map[string]bool) for rows.Next() { var txnDate string err = rows.Scan(&txnDate) if err != nil && err != sql.ErrNoRows { log.Print("getTransactionsByAccountID, no entry") log.Print(err.Error()) }else{ transactions[txnDate] = true } } return transactions } func GetClosingBalance(accountId int64) float64 { tx, _, db := connection.Connect() //Interest Paid Out (Debit) row := tx.QueryRow("select IF(amount is null, 0, SUM(amount)) as amount from transaction where txn_type in ('INTEREST_DEBIT', 'INTEREST_DEBIT_MONTHLY', 'WITHDRAW','WITHDRAW_MONTHLY') and status = 'SUCCESS' and account_id = ?", accountId) var interestPaidOut float64 var interestGained float64 err := row.Scan(&interestPaidOut) if err != nil { log.Print("GetClosingBalance: interestPaidOut") log.Print(err.Error()) return 0 } //End //Interest Gained (Credit) row = tx.QueryRow("select IF(amount is null, 0, SUM(amount)) as amount from transaction where txn_type in ('INTEREST_CREDIT', 'DEPOSIT') and status = 'SUCCESS' and account_id = ?", accountId) err = row.Scan(&interestGained) if err != nil { log.Print("GetClosingBalance: interestGained") log.Print(err.Error()) return 0 } //End connection.Close(tx, db) return interestGained - interestPaidOut } func InterestCalculationByDays() { log.Print(os.Args[1:]) dt := getDate() format := "2006-01-02" tx, ctx, db := connection.Connect() query := "select ac.scheme_id, account_id, s.xirr_rate, investor_id, initial_amount, start_date, COALESCE(end_date, '') as end_date from account ac left join scheme s on ac.scheme_id = s.scheme_id where account_status = 'ACTIVE' and s.scheme_type = 'FLEXI' and s.tenure_type = 'D' and end_date is null" rows, err := tx.Query(query) var accounts []Accounts today := dt.Format(format) for rows.Next() { var account Accounts err = rows.Scan(&account.SchemeID, &account.AccountID, &account.XIRR_RATE, &account.InvestorID, &account.InitialAmount, &account.StartDate, &account.EndDate) if err != nil { log.Print(err.Error()) } if len(account.EndDate) == 0 { account.EndDate = today } accounts = append(accounts, account) } log.Print("calculating interest") if len(accounts) > 0 { var interestEntries []InterestEntry for i := 0; i < len(accounts); i++ { var investmentStruct investmentV1.FlexiPlan var invest investmentV1.Invest var interestEntry InterestEntry investmentStruct.Principal = accounts[i].InitialAmount investmentStruct.MonthlyROI = accounts[i].XIRR_RATE investmentStruct.StartDate = accounts[i].StartDate investmentStruct.EndDate = accounts[i].EndDate end, _ := time.Parse(format, accounts[i].EndDate) start, _ := time.Parse(format, accounts[i].StartDate) diff := end.Sub(start) days := int64(diff.Hours() / 24) if days > 1 { //get only one day's calculation days = 1 } log.Print("Days:", days) log.Print(investmentStruct) //data, _ := investmentStruct.FlexiV1() investment, _ := investmentStruct.InterestCalculationByDays(days) log.Print(investment.TotalInterestEarned) if investment.IndividualInvestment.TotalInterest > 0 { interestEntry.AccountID = accounts[i].AccountID interestEntry.Type = "Deposit" interestEntry.Credit = investment.TotalInterestEarned interestEntries = append(interestEntries, interestEntry) _, err = investmentV1.CreatePassbook(interestEntry.AccountID, tx, ctx, interestEntry.Type, interestEntry.Credit, 0, 0) if err != nil { tx.Rollback() log.Print(err.Error()) } invest.AccountID = accounts[i].AccountID invest.Amount = interestEntry.Credit invest.PaymentMethod = 1 invest.PaymentGateway = 1 invest.MHP = accounts[i].SchemeID invest.ReferenceCode = investmentV1.GetRequestID() log.Print("creating CreateTransaction") _, err = investmentV1.CreateTransaction(invest, tx, ctx, "INTEREST_CREDIT", "SUCCESS") if err != nil { log.Print(err.Error()) tx.Rollback() } } else { log.Print("No Interest accrued: Account ID", accounts[i].AccountID) } } } else { log.Print("No entries are found") } connection.Close(tx, db) } func getDate() time.Time { loc, _ := time.LoadLocation("Asia/Kolkata") dt := time.Now().In(loc) return dt } func getLastDayOfMonth() int { now := time.Now() currentYear, currentMonth, _ := now.Date() currentLocation := now.Location() firstOfMonth := time.Date(currentYear, currentMonth, 1, 0, 0, 0, 0, currentLocation) return firstOfMonth.AddDate(0, 1, -1).Day() } func getFirstDayOfMonth() int { now := time.Now() currentYear, currentMonth, _ := now.Date() currentLocation := now.Location() firstOfMonth := time.Date(currentYear, currentMonth, 1, 0, 0, 0, 0, currentLocation) return firstOfMonth.Day() } func isMonthEnd() int { now := time.Now() if now.Day() == getLastDayOfMonth() { return 1 } return 0 } func isMonthStart() int { now := time.Now() if now.Day() == getFirstDayOfMonth() { return 1 } return 0 } func sendMonthEndCommunication(customers []Customers) { currentTime := time.Now() dateTime := currentTime.Format("Mon 2006-01-2 15:04:05") HtmlBody := "
" + "
" + "" + "" + "" + "
" + "
" + "CreditFair Capital" + "
·New Registration
" + "" + "" + "" + "
" + "
Monthly Withdrawal
" + "
" + dateTime + "
" + "" + "" + "" + "
" + "
Registration Summary ·
" + "" + "" + "" + "" + "" + "" + "
EmailTest
" + "
" //storage.Save(customers) log.Print("trigger email, month end") _, err := mail.Email(HtmlBody, fmt.Sprint("System - Monthly Withdrawal ", dateTime)) if err != nil { log.Print("trigger email error") log.Print(err.Error()) } } func (d Customers) String() string { v := reflect.ValueOf(d) var csvString string for i := 0; i < v.NumField(); i++ { csvString = fmt.Sprintf("%v%v,", csvString, v.Field(i).Interface()) } return csvString }