You've already forked openaccounting-server
mirror of
https://github.com/openaccounting/oa-server.git
synced 2025-12-09 00:50:59 +13:00
371 lines
7.5 KiB
Go
371 lines
7.5 KiB
Go
|
|
package db
|
||
|
|
|
||
|
|
import (
|
||
|
|
"database/sql"
|
||
|
|
"errors"
|
||
|
|
"github.com/openaccounting/oa-server/core/model/types"
|
||
|
|
"github.com/openaccounting/oa-server/core/util"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
type OrgInterface interface {
|
||
|
|
CreateOrg(*types.Org, string, []*types.Account) error
|
||
|
|
UpdateOrg(*types.Org) error
|
||
|
|
GetOrg(string, string) (*types.Org, error)
|
||
|
|
GetOrgs(string) ([]*types.Org, error)
|
||
|
|
GetOrgUserIds(string) ([]string, error)
|
||
|
|
InsertInvite(*types.Invite) error
|
||
|
|
AcceptInvite(*types.Invite, string) error
|
||
|
|
GetInvites(string) ([]*types.Invite, error)
|
||
|
|
GetInvite(string) (*types.Invite, error)
|
||
|
|
DeleteInvite(string) error
|
||
|
|
}
|
||
|
|
|
||
|
|
const orgFields = "LOWER(HEX(o.id)),o.inserted,o.updated,o.name,o.currency,o.`precision`"
|
||
|
|
const inviteFields = "i.id,LOWER(HEX(i.orgId)),i.inserted,i.updated,i.email,i.accepted"
|
||
|
|
|
||
|
|
func (db *DB) CreateOrg(org *types.Org, userId string, accounts []*types.Account) (err error) {
|
||
|
|
tx, err := db.Begin()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
defer func() {
|
||
|
|
if p := recover(); p != nil {
|
||
|
|
tx.Rollback()
|
||
|
|
panic(p) // re-throw panic after Rollback
|
||
|
|
} else if err != nil {
|
||
|
|
tx.Rollback()
|
||
|
|
} else {
|
||
|
|
err = tx.Commit()
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
org.Inserted = time.Now()
|
||
|
|
org.Updated = org.Inserted
|
||
|
|
|
||
|
|
// create org
|
||
|
|
query1 := "INSERT INTO org(id,inserted,updated,name,currency,`precision`) VALUES(UNHEX(?),?,?,?,?,?)"
|
||
|
|
|
||
|
|
res, err := tx.Exec(
|
||
|
|
query1,
|
||
|
|
org.Id,
|
||
|
|
util.TimeToMs(org.Inserted),
|
||
|
|
util.TimeToMs(org.Updated),
|
||
|
|
org.Name,
|
||
|
|
org.Currency,
|
||
|
|
org.Precision,
|
||
|
|
)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// associate user with org
|
||
|
|
query2 := "INSERT INTO userorg(userId,orgId,admin) VALUES(UNHEX(?),UNHEX(?), 1)"
|
||
|
|
|
||
|
|
res, err = tx.Exec(query2, userId, org.Id)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
_, err = res.LastInsertId()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// create Accounts: Root, Assets, Liabilities, Equity, Income, Expenses
|
||
|
|
|
||
|
|
for _, account := range accounts {
|
||
|
|
|
||
|
|
query := "INSERT INTO account(id,orgId,inserted,updated,name,parent,currency,`precision`,debitBalance) VALUES (UNHEX(?),UNHEX(?),?,?,?,UNHEX(?),?,?,?)"
|
||
|
|
|
||
|
|
if _, err = tx.Exec(
|
||
|
|
query,
|
||
|
|
account.Id,
|
||
|
|
org.Id,
|
||
|
|
util.TimeToMs(org.Inserted),
|
||
|
|
util.TimeToMs(org.Updated),
|
||
|
|
account.Name,
|
||
|
|
account.Parent,
|
||
|
|
account.Currency,
|
||
|
|
account.Precision,
|
||
|
|
account.DebitBalance,
|
||
|
|
); err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
permissionId, err := util.NewGuid()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Grant root permission to user
|
||
|
|
|
||
|
|
query3 := "INSERT INTO permission (id,userId,orgId,accountId,type,inserted,updated) VALUES(UNHEX(?),UNHEX(?),UNHEX(?),UNHEX(?),?,?,?)"
|
||
|
|
|
||
|
|
_, err = tx.Exec(
|
||
|
|
query3,
|
||
|
|
permissionId,
|
||
|
|
userId,
|
||
|
|
org.Id,
|
||
|
|
accounts[0].Id,
|
||
|
|
0,
|
||
|
|
util.TimeToMs(org.Inserted),
|
||
|
|
util.TimeToMs(org.Updated),
|
||
|
|
)
|
||
|
|
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) UpdateOrg(org *types.Org) error {
|
||
|
|
org.Updated = time.Now()
|
||
|
|
|
||
|
|
query := "UPDATE org SET updated = ?, name = ? WHERE id = UNHEX(?)"
|
||
|
|
_, err := db.Exec(
|
||
|
|
query,
|
||
|
|
util.TimeToMs(org.Updated),
|
||
|
|
org.Name,
|
||
|
|
org.Id,
|
||
|
|
)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) GetOrg(orgId string, userId string) (*types.Org, error) {
|
||
|
|
var o types.Org
|
||
|
|
var inserted int64
|
||
|
|
var updated int64
|
||
|
|
|
||
|
|
err := db.QueryRow("SELECT "+orgFields+" FROM org o JOIN userorg ON userorg.orgId = o.id WHERE o.id = UNHEX(?) AND userorg.userId = UNHEX(?)", orgId, userId).
|
||
|
|
Scan(&o.Id, &inserted, &updated, &o.Name, &o.Currency, &o.Precision)
|
||
|
|
|
||
|
|
switch {
|
||
|
|
case err == sql.ErrNoRows:
|
||
|
|
return nil, errors.New("Org not found")
|
||
|
|
case err != nil:
|
||
|
|
return nil, err
|
||
|
|
default:
|
||
|
|
o.Inserted = util.MsToTime(inserted)
|
||
|
|
o.Updated = util.MsToTime(updated)
|
||
|
|
return &o, nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) GetOrgs(userId string) ([]*types.Org, error) {
|
||
|
|
rows, err := db.Query("SELECT "+orgFields+" from org o JOIN userorg ON userorg.orgId = o.id WHERE userorg.userId = UNHEX(?)", userId)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
orgs := make([]*types.Org, 0)
|
||
|
|
|
||
|
|
for rows.Next() {
|
||
|
|
o := new(types.Org)
|
||
|
|
var inserted int64
|
||
|
|
var updated int64
|
||
|
|
|
||
|
|
err = rows.Scan(&o.Id, &inserted, &updated, &o.Name, &o.Currency, &o.Precision)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
o.Inserted = util.MsToTime(inserted)
|
||
|
|
o.Updated = util.MsToTime(updated)
|
||
|
|
|
||
|
|
orgs = append(orgs, o)
|
||
|
|
}
|
||
|
|
err = rows.Err()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return orgs, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) GetOrgUserIds(orgId string) ([]string, error) {
|
||
|
|
rows, err := db.Query("SELECT LOWER(HEX(userId)) FROM userorg WHERE orgId = UNHEX(?)", orgId)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
userIds := make([]string, 0)
|
||
|
|
|
||
|
|
for rows.Next() {
|
||
|
|
var userId string
|
||
|
|
err = rows.Scan(&userId)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
userIds = append(userIds, userId)
|
||
|
|
}
|
||
|
|
|
||
|
|
err = rows.Err()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return userIds, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) InsertInvite(invite *types.Invite) error {
|
||
|
|
invite.Inserted = time.Now()
|
||
|
|
invite.Updated = invite.Inserted
|
||
|
|
|
||
|
|
query := "INSERT INTO invite(id,orgId,inserted,updated,email,accepted) VALUES(?,UNHEX(?),?,?,?,?)"
|
||
|
|
_, err := db.Exec(
|
||
|
|
query,
|
||
|
|
invite.Id,
|
||
|
|
invite.OrgId,
|
||
|
|
util.TimeToMs(invite.Inserted),
|
||
|
|
util.TimeToMs(invite.Updated),
|
||
|
|
invite.Email,
|
||
|
|
false,
|
||
|
|
)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) AcceptInvite(invite *types.Invite, userId string) error {
|
||
|
|
invite.Updated = time.Now()
|
||
|
|
|
||
|
|
// Get root account for permission
|
||
|
|
rootAccount, err := db.GetRootAccount(invite.OrgId)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
tx, err := db.Begin()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer func() {
|
||
|
|
if p := recover(); p != nil {
|
||
|
|
tx.Rollback()
|
||
|
|
panic(p) // re-throw panic after Rollback
|
||
|
|
} else if err != nil {
|
||
|
|
tx.Rollback()
|
||
|
|
} else {
|
||
|
|
err = tx.Commit()
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
// associate user with org
|
||
|
|
query1 := "INSERT INTO userorg(userId,orgId,admin) VALUES(UNHEX(?),UNHEX(?), 0)"
|
||
|
|
|
||
|
|
_, err = tx.Exec(query1, userId, invite.OrgId)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
query2 := "UPDATE invite SET accepted = 1, updated = ? WHERE id = ?"
|
||
|
|
|
||
|
|
_, err = tx.Exec(query2, util.TimeToMs(invite.Updated), invite.Id)
|
||
|
|
|
||
|
|
// Grant root permission to user
|
||
|
|
|
||
|
|
permissionId, err := util.NewGuid()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
query3 := "INSERT INTO permission (id,userId,orgId,accountId,type,inserted,updated) VALUES(UNHEX(?),UNHEX(?),UNHEX(?),UNHEX(?),?,?,?)"
|
||
|
|
|
||
|
|
_, err = tx.Exec(
|
||
|
|
query3,
|
||
|
|
permissionId,
|
||
|
|
userId,
|
||
|
|
invite.OrgId,
|
||
|
|
rootAccount.Id,
|
||
|
|
0,
|
||
|
|
util.TimeToMs(invite.Updated),
|
||
|
|
util.TimeToMs(invite.Updated),
|
||
|
|
)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) GetInvites(orgId string) ([]*types.Invite, error) {
|
||
|
|
// don't include expired invoices
|
||
|
|
cutoff := util.TimeToMs(time.Now()) - 7*24*60*60*1000
|
||
|
|
|
||
|
|
rows, err := db.Query("SELECT "+inviteFields+" FROM invite i WHERE orgId = UNHEX(?) AND inserted > ?", orgId, cutoff)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
invites := make([]*types.Invite, 0)
|
||
|
|
|
||
|
|
for rows.Next() {
|
||
|
|
i := new(types.Invite)
|
||
|
|
var inserted int64
|
||
|
|
var updated int64
|
||
|
|
|
||
|
|
err = rows.Scan(&i.Id, &i.OrgId, &inserted, &updated, &i.Email, &i.Accepted)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
i.Inserted = util.MsToTime(inserted)
|
||
|
|
i.Updated = util.MsToTime(updated)
|
||
|
|
|
||
|
|
invites = append(invites, i)
|
||
|
|
}
|
||
|
|
err = rows.Err()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return invites, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) GetInvite(id string) (*types.Invite, error) {
|
||
|
|
var i types.Invite
|
||
|
|
var inserted int64
|
||
|
|
var updated int64
|
||
|
|
|
||
|
|
err := db.QueryRow("SELECT "+inviteFields+" FROM invite i WHERE i.id = ?", id).
|
||
|
|
Scan(&i.Id, &i.OrgId, &inserted, &updated, &i.Email, &i.Accepted)
|
||
|
|
|
||
|
|
switch {
|
||
|
|
case err == sql.ErrNoRows:
|
||
|
|
return nil, errors.New("Invite not found")
|
||
|
|
case err != nil:
|
||
|
|
return nil, err
|
||
|
|
default:
|
||
|
|
i.Inserted = util.MsToTime(inserted)
|
||
|
|
i.Updated = util.MsToTime(updated)
|
||
|
|
return &i, nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (db *DB) DeleteInvite(id string) error {
|
||
|
|
query := "DELETE FROM invite WHERE id = ?"
|
||
|
|
_, err := db.Exec(
|
||
|
|
query,
|
||
|
|
id,
|
||
|
|
)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|