theorose49 db0f857f0a
All checks were successful
build-and-push / build (push) Successful in 33s
feat(project): 계약범위를 글/그림 자유 텍스트 2칸으로 변경 (드롭다운 제거)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:23:38 +09:00

160 lines
6.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package models
import (
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
)
// Company → Product → Version is the consulting hierarchy. A Project is created
// per (company, product, version) engagement and is otherwise independent.
type Company struct {
Base
Name string `json:"name"`
Code string `json:"code"`
Note string `json:"note"`
CreatedAt time.Time `json:"createdAt"`
}
func (m *Company) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
type Product struct {
Base
CompanyID string `gorm:"index" json:"companyId"`
Name string `json:"name"`
Code string `json:"code"`
CreatedAt time.Time `json:"createdAt"`
}
func (m *Product) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
type Version struct {
Base
ProductID string `gorm:"index" json:"productId"`
Label string `json:"label"`
CreatedAt time.Time `json:"createdAt"`
}
func (m *Version) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// Project is the core engagement record. The bracketed [admin-only] fields in the
// spec live on Contract / PaymentSplit, not here, so Project is safe to expose to
// any member who belongs to it.
type Project struct {
Base
Name string `json:"name"`
CompanyID string `gorm:"index" json:"companyId"`
ProductID string `json:"productId"`
VersionID string `json:"versionId"`
CompanyName string `json:"companyName"` // denormalized 업체명 snapshot
ProductName string `json:"productName"`
VersionName string `json:"versionName"`
ConsultingType string `json:"consultingType"` // 컨설팅 종류
Country string `json:"country"` // 제출 국가
// 계약 범위: 글/그림 각각 자유 입력(무엇을 포함하는지 텍스트로 기술).
ScopeText string `json:"scopeText"` // 글 계약 범위
ScopeGraphic string `json:"scopeGraphic"` // 그림 계약 범위
PMEmail string `json:"pmEmail"` // 프로젝트 PM
Cautions string `json:"cautions"` // 주의사항 (구성원 공개)
Status string `json:"status"` // planned | active | hold | done | dropped
StartDate string `json:"startDate"`
DueDate string `json:"dueDate"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *Project) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// ProjectMember links a Member to a Project with a portion (기여도, 0100) that
// drives incentive distribution. Role is a free label (작업자/리뷰어/...).
type ProjectMember struct {
Base
ProjectID string `gorm:"index" json:"projectId"`
MemberEmail string `gorm:"index" json:"memberEmail"`
Portion float64 `json:"portion"` // 기여도 percent
Role string `json:"role"`
CreatedAt time.Time `json:"createdAt"`
}
func (m *ProjectMember) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// ClientContact is a 업체 담당자 (직무/이름/연락처).
type ClientContact struct {
Base
ProjectID string `gorm:"index" json:"projectId"`
Name string `json:"name"`
Title string `json:"title"` // 직무
Phone string `json:"phone"`
Email string `json:"email"`
}
func (m *ClientContact) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// ProjectTask feeds both the Gantt and Kanban views and matches the real
// calendar via Start/End (YYYY-MM-DD). Lane is the Kanban column.
type ProjectTask struct {
Base
ProjectID string `gorm:"index" json:"projectId"`
Title string `json:"title"`
Lane string `json:"lane"` // todo | doing | review | done
Start string `json:"start"` // YYYY-MM-DD
End string `json:"end"`
Assignee string `json:"assignee"`
OrderIdx int `json:"orderIdx"`
Progress int `json:"progress"` // 0100
DependsOn datatypes.JSONSlice[string] `json:"dependsOn"`
Color string `json:"color"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *ProjectTask) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// Contract holds the [admin-only] commercial terms of a project. BE is the
// break-even floor (손해가 안 나는 최소 금액). Exposed ONLY to admins.
type Contract struct {
Base
ProjectID string `gorm:"uniqueIndex" json:"projectId"`
TotalAmount float64 `json:"totalAmount"` // 계약 금액
BEAmount float64 `json:"beAmount"` // BE (break-even 최소 금액)
AdminCaution string `json:"adminCaution"` // 관리자 주의사항
Memo string `json:"memo"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *Contract) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// ContractFile is an [admin-only] attachment (계약서/기타 자료, 여러개 가능) in S3.
type ContractFile struct {
Base
ProjectID string `gorm:"index" json:"projectId"`
Kind string `json:"kind"` // contract | other
Filename string `json:"filename"`
S3Key string `json:"s3Key"`
Size int64 `json:"size"`
UploadedBy string `json:"uploadedBy"`
CreatedAt time.Time `json:"createdAt"`
}
func (m *ContractFile) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// PaymentSplit is one [admin-only] custom installment of the contract amount.
// Amounts arrive in arbitrary splits across dates, so every field is editable.
type PaymentSplit struct {
Base
ProjectID string `gorm:"index" json:"projectId"`
Label string `json:"label"`
Amount float64 `json:"amount"`
ExpectedDate string `json:"expectedDate"` // 예상 일정
PaidDate string `json:"paidDate"` // 실제 입금일 (빈값=미입금)
Paid bool `json:"paid"`
Memo string `json:"memo"`
OrderIdx int `json:"orderIdx"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *PaymentSplit) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }