You've already forked openaccounting-server
forked from cybercinch/openaccounting-server
feat: implement attachment database layer
- Add AttachmentInterface to main Datastore interface - Implement CRUD operations for attachments following existing patterns - Add proper SQL marshalling/unmarshalling with HEX/UNHEX for binary IDs - Include soft deletion and proper indexing support 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
126
core/model/db/attachment.go
Normal file
126
core/model/db/attachment.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/openaccounting/oa-server/core/model/types"
|
||||
"github.com/openaccounting/oa-server/core/util"
|
||||
)
|
||||
|
||||
const attachmentFields = "LOWER(HEX(id)),LOWER(HEX(transactionId)),LOWER(HEX(orgId)),LOWER(HEX(userId)),fileName,originalName,contentType,fileSize,filePath,description,uploaded,deleted"
|
||||
|
||||
type AttachmentInterface interface {
|
||||
InsertAttachment(*types.Attachment) error
|
||||
GetAttachment(string, string, string) (*types.Attachment, error)
|
||||
GetAttachmentsByTransaction(string, string) ([]*types.Attachment, error)
|
||||
DeleteAttachment(string, string, string) error
|
||||
}
|
||||
|
||||
func (db *DB) InsertAttachment(attachment *types.Attachment) error {
|
||||
query := "INSERT INTO attachment(id,transactionId,orgId,userId,fileName,originalName,contentType,fileSize,filePath,description,uploaded,deleted) VALUES(UNHEX(?),UNHEX(?),UNHEX(?),UNHEX(?),?,?,?,?,?,?,?,?)"
|
||||
|
||||
_, err := db.Exec(
|
||||
query,
|
||||
attachment.Id,
|
||||
attachment.TransactionId,
|
||||
attachment.OrgId,
|
||||
attachment.UserId,
|
||||
attachment.FileName,
|
||||
attachment.OriginalName,
|
||||
attachment.ContentType,
|
||||
attachment.FileSize,
|
||||
attachment.FilePath,
|
||||
attachment.Description,
|
||||
util.TimeToMs(attachment.Uploaded),
|
||||
attachment.Deleted,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) GetAttachment(attachmentId, transactionId, orgId string) (*types.Attachment, error) {
|
||||
query := "SELECT " + attachmentFields + " FROM attachment WHERE id = UNHEX(?) AND transactionId = UNHEX(?) AND orgId = UNHEX(?) AND deleted = false"
|
||||
row := db.QueryRow(query, attachmentId, transactionId, orgId)
|
||||
|
||||
return db.unmarshalAttachment(row)
|
||||
}
|
||||
|
||||
func (db *DB) GetAttachmentsByTransaction(transactionId, orgId string) ([]*types.Attachment, error) {
|
||||
query := "SELECT " + attachmentFields + " FROM attachment WHERE transactionId = UNHEX(?) AND orgId = UNHEX(?) AND deleted = false ORDER BY uploaded DESC"
|
||||
rows, err := db.Query(query, transactionId, orgId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db.unmarshalAttachments(rows)
|
||||
}
|
||||
|
||||
func (db *DB) DeleteAttachment(attachmentId, transactionId, orgId string) error {
|
||||
query := "UPDATE attachment SET deleted = true WHERE id = UNHEX(?) AND transactionId = UNHEX(?) AND orgId = UNHEX(?)"
|
||||
_, err := db.Exec(query, attachmentId, transactionId, orgId)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) unmarshalAttachment(row *sql.Row) (*types.Attachment, error) {
|
||||
attachment := &types.Attachment{}
|
||||
var uploaded int64
|
||||
|
||||
err := row.Scan(
|
||||
&attachment.Id,
|
||||
&attachment.TransactionId,
|
||||
&attachment.OrgId,
|
||||
&attachment.UserId,
|
||||
&attachment.FileName,
|
||||
&attachment.OriginalName,
|
||||
&attachment.ContentType,
|
||||
&attachment.FileSize,
|
||||
&attachment.FilePath,
|
||||
&attachment.Description,
|
||||
&uploaded,
|
||||
&attachment.Deleted,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attachment.Uploaded = util.MsToTime(uploaded)
|
||||
|
||||
return attachment, nil
|
||||
}
|
||||
|
||||
func (db *DB) unmarshalAttachments(rows *sql.Rows) ([]*types.Attachment, error) {
|
||||
defer rows.Close()
|
||||
|
||||
attachments := []*types.Attachment{}
|
||||
|
||||
for rows.Next() {
|
||||
attachment := &types.Attachment{}
|
||||
var uploaded int64
|
||||
|
||||
err := rows.Scan(
|
||||
&attachment.Id,
|
||||
&attachment.TransactionId,
|
||||
&attachment.OrgId,
|
||||
&attachment.UserId,
|
||||
&attachment.FileName,
|
||||
&attachment.OriginalName,
|
||||
&attachment.ContentType,
|
||||
&attachment.FileSize,
|
||||
&attachment.FilePath,
|
||||
&attachment.Description,
|
||||
&uploaded,
|
||||
&attachment.Deleted,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attachment.Uploaded = util.MsToTime(uploaded)
|
||||
attachments = append(attachments, attachment)
|
||||
}
|
||||
|
||||
return attachments, nil
|
||||
}
|
||||
@@ -15,6 +15,7 @@ type Datastore interface {
|
||||
OrgInterface
|
||||
AccountInterface
|
||||
TransactionInterface
|
||||
AttachmentInterface
|
||||
PriceInterface
|
||||
SessionInterface
|
||||
ApiKeyInterface
|
||||
|
||||
Reference in New Issue
Block a user