spin-backend/internal/models/attendance.go
theorose49 f83724b995
All checks were successful
build-and-push / build (push) Successful in 39s
feat: spin 백엔드 전체 구현 (근무·프로젝트·인센티브·회계)
- config/db/storage/auth/router/perms: eQMS 규약 미러링, 권한 2-tier
  (관리자 전체 / 구성원 본인·신청만), oauth2-proxy 헤더 인증 + DEV_AUTH mock
- 모델: 구성원/부서, 근무(출퇴근·휴가·공가·초과), 프로젝트(회사/제품/버전·
  작업자portion·담당자·태스크·계약·첨부·분할입금), 인센티브(설정·단계·
  유저배분·분기정산), 회계(거래·세금)
- internal/worktime: 근로기준법 월 집계 엔진
- internal/incentive: BE/non-BE × 계약금/중도금/잔금 3단계 계산 + 시뮬레이션
- 시드 데이터, Go 멀티스테이지 Dockerfile
- ADMIN_GROUPS 기본값 'admin' (전 내부 앱 공통 그룹)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:57:35 +09:00

110 lines
4.1 KiB
Go

package models
import (
"time"
"gorm.io/gorm"
)
// Request status shared by leave/overtime approval workflows.
const (
StatusPending = "pending"
StatusApproved = "approved"
StatusRejected = "rejected"
StatusCanceled = "canceled"
)
// Leave types (한국 근로기준법 기준). 연차 draws down the annual balance; the rest
// are 인정/무급 categories that still count toward worked time where applicable.
const (
LeaveAnnual = "annual" // 연차
LeaveHalfAM = "half_am" // 오전 반차
LeaveHalfPM = "half_pm" // 오후 반차
LeavePublic = "public" // 공가
LeaveSick = "sick" // 병가
LeaveFamily = "family" // 경조사
LeaveUnpaid = "unpaid" // 무급
)
// Attendance is one day's clock record for a member (date is YYYY-MM-DD in KST).
type Attendance struct {
Base
MemberEmail string `gorm:"index" json:"memberEmail"`
Date string `gorm:"index" json:"date"` // YYYY-MM-DD
ClockIn *time.Time `json:"clockIn"`
ClockOut *time.Time `json:"clockOut"`
WorkMinutes int `json:"workMinutes"` // computed net worked minutes
Source string `json:"source"` // web | admin | auto
Note string `json:"note"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *Attendance) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// LeaveRequest is a member-submitted leave application. Members may only create;
// admins approve/reject. Days is fractional to support 반차 (0.5).
type LeaveRequest struct {
Base
MemberEmail string `gorm:"index" json:"memberEmail"`
Type string `json:"type"`
StartDate string `json:"startDate"` // YYYY-MM-DD
EndDate string `json:"endDate"`
Days float64 `json:"days"`
Reason string `json:"reason"`
Status string `gorm:"index" json:"status"`
Approver string `json:"approver"`
DecidedAt *time.Time `json:"decidedAt"`
DecisionMemo string `json:"decisionMemo"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *LeaveRequest) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// OvertimeRequest is a member-submitted overtime application (관리자만 확인/승인).
type OvertimeRequest struct {
Base
MemberEmail string `gorm:"index" json:"memberEmail"`
Date string `json:"date"`
Minutes int `json:"minutes"`
Reason string `json:"reason"`
Status string `gorm:"index" json:"status"`
Approver string `json:"approver"`
DecidedAt *time.Time `json:"decidedAt"`
DecisionMemo string `json:"decisionMemo"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *OvertimeRequest) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// WorkPolicy is the company-wide work-hours policy used by the monthly timesheet
// roll-up. Singleton-ish (one active row); admin editable.
type WorkPolicy struct {
Base
Name string `json:"name"`
WeeklyHours float64 `json:"weeklyHours"` // 주 소정근로시간 (기본 40)
DailyStandardMin int `json:"dailyStandardMin"` // 일 소정근로분 (기본 480)
CoreStart string `json:"coreStart"` // "09:00"
CoreEnd string `json:"coreEnd"` // "18:00"
LunchMinutes int `json:"lunchMinutes"` // 휴게(기본 60)
AnnualLeaveBase float64 `json:"annualLeaveBase"` // 1년 미만/이상 기준 부여일
Active bool `json:"active"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (m *WorkPolicy) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
// LeaveBalance tracks per-member annual leave usage for a given year.
type LeaveBalance struct {
Base
MemberEmail string `gorm:"index" json:"memberEmail"`
Year int `gorm:"index" json:"year"`
Granted float64 `json:"granted"`
Used float64 `json:"used"`
}
func (m *LeaveBalance) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }